Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(information): utilisation de tiptap sur les pages infos #1472

Draft
wants to merge 2 commits into
base: feat/allow-multiple-publish
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions targets/frontend/src/components/data/Head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";
import Checkbox from "@mui/material/Checkbox";
import * as React from "react";
import { Data, HeadCell } from "./type";

export type EnhancedTableProps<T extends Data> = {
readonly headCells: HeadCell<T>[];
numSelected: number;
onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void;
rowCount: number;
};

export const EnhancedTableHead = <T extends Data>({
onSelectAllClick,
numSelected,
rowCount,
headCells,
}: EnhancedTableProps<T>) => {
return (
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
color="primary"
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={onSelectAllClick}
/>
</TableCell>
{headCells.map((headCell) => (
<TableCell key={headCell.id} padding="normal">
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
};
64 changes: 64 additions & 0 deletions targets/frontend/src/components/data/PublishModal/ListContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
CircularProgress,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from "@mui/material";
import { Content } from "./index";
import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";

type ContentWithProgression = Content & {
status: "pending" | "processing" | "done" | "error";
};

type ListContentProps = {
contents: ContentWithProgression[];
};

const Status = ({ status }: Pick<ContentWithProgression, "status">) => {
switch (status) {
case "pending":
return <PauseCircleOutlineIcon color="disabled" />;
case "processing":
return <CircularProgress color="info" size="1.5rem" />;
case "done":
return <CheckCircleOutlineIcon color="success" />;
case "error":
return <ErrorOutlineIcon color="error" />;
}
};

export function ListContent({ contents }: ListContentProps): JSX.Element {
return (
<TableContainer sx={{ maxHeight: 440 }}>
<Table stickyHeader>
<TableHead>
<TableRow>
<TableCell>Titre</TableCell>
<TableCell align="right">Progression</TableCell>
</TableRow>
</TableHead>
<TableBody>
{contents.map((row) => (
<TableRow
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.title}
</TableCell>
<TableCell align="right">
<Status status={row.status} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
151 changes: 151 additions & 0 deletions targets/frontend/src/components/data/PublishModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Alert, Box, Button, Modal, Stack, Typography } from "@mui/material";
import { styled } from "@mui/system";
import { fr } from "@codegouvfr/react-dsfr";
import { usePublishMutation } from "../../../modules/documents/components/publish.mutation";
import { Source } from "../type";
import { useEffect, useState } from "react";
import { ListContent } from "./ListContent";

export type Content = {
id: string;
title: string;
};

export type PublishModalProps = {
source: Source;
contents: Content[];
open: boolean;
onClose: () => void;
onCancel: () => void;
};

type ContentProgression = {
[id: string]: "pending" | "processing" | "done" | "error";
};

export function PublishModal({
contents,
open,
onClose,
onCancel,
source,
}: PublishModalProps): JSX.Element {
const publish = usePublishMutation();

const [contentProgression, setContentProgression] =
useState<ContentProgression>({});
const [processing, setProcessing] = useState<"waiting" | "process" | "done">(
"waiting"
);

useEffect(() => {
setContentProgression(
contents.reduce((acc, item) => {
acc[item.id] = "pending";
return acc;
}, {} as ContentProgression)
);
}, [contents]);

const onValidate = async () => {
setProcessing("process");
let currentProgression = contents.reduce((acc, item) => {
acc[item.id] = "pending";
return acc;
}, {} as ContentProgression);
for (const content of contents) {
currentProgression = {
...currentProgression,
[content.id]: "processing",
};
setContentProgression(currentProgression);
try {
const { data, error } = await publish({
id: content.id,
source: source,
});

if (error || data === undefined) {
currentProgression = {
...currentProgression,
[content.id]: "error",
};
setContentProgression(currentProgression);
} else {
currentProgression = {
...currentProgression,
[content.id]: "done",
};
setContentProgression(currentProgression);
}
} catch (e) {
currentProgression = {
...currentProgression,
[content.id]: "error",
};
setContentProgression(currentProgression);
}
}
setProcessing("done");
};

return (
<Modal open={open} onClose={onClose}>
<StyledBox>
<Typography variant="h4" component="h2" mb={4}>
Publication de contenu
</Typography>
<Stack direction="column" spacing={2}>
<p>
Vous êtes sur le point de mettre à jour les {contents.length}{" "}
contenus suivant.
</p>
<ListContent
contents={contents.map((item) => ({
...item,
status: contentProgression[item.id] ?? "pending",
}))}
/>
<p>
Ces derniers seront disponible sur le site après une mise en
production des données. Êtes-vous sûr de vouloir publier ces
contenus ?
</p>
</Stack>
<Stack direction="row" spacing={2} mt={4} justifyContent="end">
{processing === "waiting" && (
<Button variant="outlined" onClick={onCancel}>
Annuler
</Button>
)}
{processing === "waiting" && (
<Button variant="contained" onClick={onValidate}>
Oui
</Button>
)}
{processing === "process" && (
<Alert severity="warning">
Merci de ne pas fermer cette fenêtre avant la fin de la mise à
jour.
</Alert>
)}
{processing === "done" && (
<Button variant="outlined" onClick={onClose}>
Fermer
</Button>
)}
</Stack>
</StyledBox>
</Modal>
);
}

const StyledBox = styled(Box)({
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 800,
backgroundColor: `${fr.colors.decisions.background.default.grey.default}`,
padding: `${fr.spacing("8v")}`,
});
79 changes: 79 additions & 0 deletions targets/frontend/src/components/data/Toolbard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Toolbar from "@mui/material/Toolbar";
import { alpha } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
import PublishIcon from "@mui/icons-material/Publish";
import * as React from "react";
import { Button, FormGroup, Stack, TextField } from "@mui/material";

interface EnhancedTableToolbarProps {
numSelected: number;
onClickPublish: () => void;
onClickCreation: () => void;
setSearch: (value: string | undefined) => void;
customFilters?: React.ReactNode;
}

export const EnhancedTableToolbar = ({
numSelected,
onClickPublish,
onClickCreation,
setSearch,
customFilters = undefined,
}: EnhancedTableToolbarProps) => {
return (
<Toolbar
sx={[
{
pl: { sm: 2 },
pr: { xs: 1, sm: 1 },
},
numSelected > 0 && {
bgcolor: (theme) =>
alpha(
theme.palette.primary.main,
theme.palette.action.activatedOpacity
),
},
]}
>
{numSelected > 0 ? (
<Typography
sx={{ flex: "1 1 100%" }}
color="inherit"
variant="subtitle1"
component="div"
>
{numSelected} contenu{numSelected > 1 ? "s" : ""} sélectionné
{numSelected > 1 ? "s" : ""}
</Typography>
) : (
<FormGroup row={true} style={{ flex: "1 1 100%" }}>
<TextField
label="Recherche"
variant="outlined"
size="small"
onChange={(event) => {
const value = event.target.value;
setSearch(value ? `%${value}%` : undefined);
}}
data-testid="list-search"
/>
{customFilters}
</FormGroup>
)}
{numSelected > 0 ? (
<Button
variant="contained"
startIcon={<PublishIcon />}
onClick={onClickPublish}
>
Publier
</Button>
) : (
<Button variant="contained" color="success" onClick={onClickCreation}>
Créer
</Button>
)}
</Toolbar>
);
};
Loading
Loading