-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
785 additions
and
406 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
64
targets/frontend/src/components/data/PublishModal/ListContent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
151
targets/frontend/src/components/data/PublishModal/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")}`, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
Oops, something went wrong.