Skip to content

Commit

Permalink
feat: add publish function on list
Browse files Browse the repository at this point in the history
  • Loading branch information
m-maillot committed Sep 19, 2024
1 parent 441744d commit 5cf0cfe
Show file tree
Hide file tree
Showing 27 changed files with 785 additions and 406 deletions.
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

0 comments on commit 5cf0cfe

Please sign in to comment.