From 5cf0cfeb01b0862e4addeb047ce0e01e39c5a046 Mon Sep 17 00:00:00 2001 From: Martial Maillot Date: Wed, 18 Sep 2024 11:39:01 +0200 Subject: [PATCH] feat: add publish function on list --- targets/frontend/src/components/data/Head.tsx | 40 +++++ .../data/PublishModal/ListContent.tsx | 64 ++++++++ .../components/data/PublishModal/index.tsx | 151 +++++++++++++++++ .../frontend/src/components/data/Toolbard.tsx | 79 +++++++++ .../frontend/src/components/data/index.tsx | 146 +++++++++++++++++ targets/frontend/src/components/data/type.ts | 18 ++ .../agreements/components/Edition/index.tsx | 12 +- .../components/Edition/publish.mutation.ts | 25 --- .../components/List/Filter/index.tsx | 80 +++++++++ .../agreements/components/List/Row.tsx | 36 ---- .../agreements/components/List/index.tsx | 154 ++++++++---------- .../agreements/components/List/list.query.ts | 11 +- .../documents/components/publish.mutation.ts | 47 ++++++ .../informations/components/List/index.tsx | 42 +++++ .../list.query.ts} | 4 +- .../modules/informations/components/index.ts | 2 +- .../informationsEdit/InformationsEdit.tsx | 11 +- .../publishInformation.mutation.ts | 28 ---- .../informationsList/InformationsList.tsx | 68 -------- .../informationsList/InformationsRow.tsx | 28 ---- .../components/informationsList/index.ts | 1 - .../models/components/Edition/index.tsx | 4 +- .../components/Edition/publish.mutation.ts | 25 --- .../modules/models/components/List/Row.tsx | 28 ---- .../modules/models/components/List/index.tsx | 82 +++------- .../frontend/src/pages/informations/index.tsx | 4 +- targets/hasura/metadata/actions.yaml | 1 + 27 files changed, 785 insertions(+), 406 deletions(-) create mode 100644 targets/frontend/src/components/data/Head.tsx create mode 100644 targets/frontend/src/components/data/PublishModal/ListContent.tsx create mode 100644 targets/frontend/src/components/data/PublishModal/index.tsx create mode 100644 targets/frontend/src/components/data/Toolbard.tsx create mode 100644 targets/frontend/src/components/data/index.tsx create mode 100644 targets/frontend/src/components/data/type.ts delete mode 100644 targets/frontend/src/modules/agreements/components/Edition/publish.mutation.ts create mode 100644 targets/frontend/src/modules/agreements/components/List/Filter/index.tsx delete mode 100644 targets/frontend/src/modules/agreements/components/List/Row.tsx create mode 100644 targets/frontend/src/modules/documents/components/publish.mutation.ts create mode 100644 targets/frontend/src/modules/informations/components/List/index.tsx rename targets/frontend/src/modules/informations/components/{informationsList/InformationsList.query.ts => List/list.query.ts} (89%) delete mode 100644 targets/frontend/src/modules/informations/components/informationsEdit/publishInformation.mutation.ts delete mode 100644 targets/frontend/src/modules/informations/components/informationsList/InformationsList.tsx delete mode 100644 targets/frontend/src/modules/informations/components/informationsList/InformationsRow.tsx delete mode 100644 targets/frontend/src/modules/informations/components/informationsList/index.ts delete mode 100644 targets/frontend/src/modules/models/components/Edition/publish.mutation.ts delete mode 100644 targets/frontend/src/modules/models/components/List/Row.tsx diff --git a/targets/frontend/src/components/data/Head.tsx b/targets/frontend/src/components/data/Head.tsx new file mode 100644 index 0000000000..56ce7bc71e --- /dev/null +++ b/targets/frontend/src/components/data/Head.tsx @@ -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 = { + readonly headCells: HeadCell[]; + numSelected: number; + onSelectAllClick: (event: React.ChangeEvent) => void; + rowCount: number; +}; + +export const EnhancedTableHead = ({ + onSelectAllClick, + numSelected, + rowCount, + headCells, +}: EnhancedTableProps) => { + return ( + + + + 0 && numSelected < rowCount} + checked={rowCount > 0 && numSelected === rowCount} + onChange={onSelectAllClick} + /> + + {headCells.map((headCell) => ( + + {headCell.label} + + ))} + + + ); +}; diff --git a/targets/frontend/src/components/data/PublishModal/ListContent.tsx b/targets/frontend/src/components/data/PublishModal/ListContent.tsx new file mode 100644 index 0000000000..d38dd7381c --- /dev/null +++ b/targets/frontend/src/components/data/PublishModal/ListContent.tsx @@ -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) => { + switch (status) { + case "pending": + return ; + case "processing": + return ; + case "done": + return ; + case "error": + return ; + } +}; + +export function ListContent({ contents }: ListContentProps): JSX.Element { + return ( + + + + + Titre + Progression + + + + {contents.map((row) => ( + + + {row.title} + + + + + + ))} + +
+
+ ); +} diff --git a/targets/frontend/src/components/data/PublishModal/index.tsx b/targets/frontend/src/components/data/PublishModal/index.tsx new file mode 100644 index 0000000000..ae3b5ecc04 --- /dev/null +++ b/targets/frontend/src/components/data/PublishModal/index.tsx @@ -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({}); + 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 ( + + + + Publication de contenu + + +

+ Vous êtes sur le point de mettre à jour les {contents.length}{" "} + contenus suivant. +

+ ({ + ...item, + status: contentProgression[item.id] ?? "pending", + }))} + /> +

+ 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 ? +

+
+ + {processing === "waiting" && ( + + )} + {processing === "waiting" && ( + + )} + {processing === "process" && ( + + Merci de ne pas fermer cette fenêtre avant la fin de la mise à + jour. + + )} + {processing === "done" && ( + + )} + +
+
+ ); +} + +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")}`, +}); diff --git a/targets/frontend/src/components/data/Toolbard.tsx b/targets/frontend/src/components/data/Toolbard.tsx new file mode 100644 index 0000000000..4ed6682047 --- /dev/null +++ b/targets/frontend/src/components/data/Toolbard.tsx @@ -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 ( + 0 && { + bgcolor: (theme) => + alpha( + theme.palette.primary.main, + theme.palette.action.activatedOpacity + ), + }, + ]} + > + {numSelected > 0 ? ( + + {numSelected} contenu{numSelected > 1 ? "s" : ""} sélectionné + {numSelected > 1 ? "s" : ""} + + ) : ( + + { + const value = event.target.value; + setSearch(value ? `%${value}%` : undefined); + }} + data-testid="list-search" + /> + {customFilters} + + )} + {numSelected > 0 ? ( + + ) : ( + + )} + + ); +}; diff --git a/targets/frontend/src/components/data/index.tsx b/targets/frontend/src/components/data/index.tsx new file mode 100644 index 0000000000..e9bcb08f9b --- /dev/null +++ b/targets/frontend/src/components/data/index.tsx @@ -0,0 +1,146 @@ +import * as React from "react"; +import { useState } from "react"; +import Box from "@mui/material/Box"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableRow from "@mui/material/TableRow"; +import Checkbox from "@mui/material/Checkbox"; +import { EnhancedTableToolbar } from "./Toolbard"; +import { EnhancedTableHead } from "./Head"; +import { Data, HeadCell, Source } from "./type"; +import { PublishModal } from "./PublishModal"; + +type Props = { + source: Source; + readonly headCells: HeadCell[]; + rows: T[]; + onClickItem: (id: string) => void; + onClickCreation: () => void; + setSearch: (value: string | undefined) => void; + customFilters?: React.ReactNode; +}; + +type ItemCheck = { + [id: string]: boolean; +}; + +export const EnhancedTable = ({ + source, + headCells, + rows, + onClickItem, + onClickCreation, + setSearch, + customFilters, +}: Props) => { + const [itemsCheck, setItemCheck] = useState({}); + const [isOpen, showModal] = useState(false); + + const handleSelectAllClick = (event: React.ChangeEvent) => { + setItemCheck( + rows.reduce((acc, item) => { + acc[item.id] = event.target.checked; + return acc; + }, {} as ItemCheck) + ); + }; + + const handleItemCheck = (id: string, checked: boolean) => { + setItemCheck({ + ...itemsCheck, + [id]: checked, + }); + }; + + const handleClick = (id: string) => { + const atLeastOneChecked = Object.values(itemsCheck).some( + (checked) => checked + ); + if (atLeastOneChecked) { + handleItemCheck(id, !itemsCheck[id]); + } else { + onClickItem(id); + } + }; + + const numSelected = (): number => { + return Object.values(itemsCheck).filter((item) => item).length; + }; + + return ( + + showModal(true)} + onClickCreation={onClickCreation} + setSearch={setSearch} + customFilters={customFilters} + /> + + + + + {rows.map((row, index) => { + const isItemSelected = itemsCheck[row.id] ?? false; + const labelId = `${source}-table-checkbox-${index}`; + + return ( + + + + handleItemCheck(row.id, event.target.checked) + } + /> + + {headCells.map((head) => ( + handleClick(row.id)} + > + {head.render + ? head.render(row[head.dataIndex]) + : (row[head.dataIndex] as React.ReactNode)} + + ))} + + ); + })} + +
+
+ {isOpen && ( + value) + .map(([key, _]) => { + const find = rows.find((item) => item.id === key)!!; + return { + id: find.id, + title: find.title, + }; + })} + open={isOpen} + onCancel={() => showModal(false)} + onClose={() => showModal(false)} + /> + )} +
+ ); +}; diff --git a/targets/frontend/src/components/data/type.ts b/targets/frontend/src/components/data/type.ts new file mode 100644 index 0000000000..673494ab92 --- /dev/null +++ b/targets/frontend/src/components/data/type.ts @@ -0,0 +1,18 @@ +export type Data = { + id: string; + title: string; + [key: string]: unknown; +}; + +export type HeadCell = { + id: string; + dataIndex: keyof T; + label: string; + render?: (value: T[HeadCell["dataIndex"]]) => React.ReactNode; +}; + +export type Source = + | "information" + | "modeles_de_courriers" + | "contributions" + | "conventions_collectives"; diff --git a/targets/frontend/src/modules/agreements/components/Edition/index.tsx b/targets/frontend/src/modules/agreements/components/Edition/index.tsx index 0602c26996..4e27263b16 100644 --- a/targets/frontend/src/modules/agreements/components/Edition/index.tsx +++ b/targets/frontend/src/modules/agreements/components/Edition/index.tsx @@ -11,13 +11,12 @@ import React from "react"; import { useAgreementUpdateMutation } from "./agreement.mutation"; import { useAgreementQuery } from "./agreement.query"; import { AgreementForm } from "../Common"; -import { usePublishMutation } from "./publish.mutation"; +import { usePublishMutation } from "../../../documents/components/publish.mutation"; import { useAgreementDeleteMutation } from "./delete.mutation"; import { useRouter } from "next/router"; import Dialog from "@mui/material/Dialog"; import DialogTitle from "@mui/material/DialogTitle"; import DialogContent from "@mui/material/DialogContent"; -import agreements from "../../../../pages/agreements"; type Props = { id: string; @@ -83,7 +82,10 @@ export const AgreementEdition = ({ id }: Props): React.ReactElement => { }} onPublish={async () => { if (data?.id) { - await publish(data.id); + await publish({ + id: data.id, + source: "conventions_collectives", + }); } else { throw new Error( "Aucune convention collective à publier n'a été détectée" @@ -125,9 +127,7 @@ export const AgreementEdition = ({ id }: Props): React.ReactElement => { conventions collectives -

- Voulez-vous quand même la supprimer maintenant ? -

+

Voulez-vous quand même la supprimer maintenant ?

diff --git a/targets/frontend/src/modules/agreements/components/Edition/publish.mutation.ts b/targets/frontend/src/modules/agreements/components/Edition/publish.mutation.ts deleted file mode 100644 index 1062c6ece7..0000000000 --- a/targets/frontend/src/modules/agreements/components/Edition/publish.mutation.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { OperationResult, useMutation } from "urql"; - -export const publishMutation = ` -mutation publish_information( - $id: uuid! -) { - publish(id: $id, source: "conventions_collectives") { - cdtnId - } -} -`; - -export type PublishMutationResult = (id: string) => Promise; - -export const usePublishMutation = (): PublishMutationResult => { - const [, execute] = useMutation(publishMutation); - const resultFunction = async (id: string) => { - const result = await execute({ id }); - if (result.error) { - throw new Error(result.error.message); - } - return result; - }; - return resultFunction; -}; diff --git a/targets/frontend/src/modules/agreements/components/List/Filter/index.tsx b/targets/frontend/src/modules/agreements/components/List/Filter/index.tsx new file mode 100644 index 0000000000..36ab567d91 --- /dev/null +++ b/targets/frontend/src/modules/agreements/components/List/Filter/index.tsx @@ -0,0 +1,80 @@ +import * as React from "react"; +import { Theme, useTheme } from "@mui/material/styles"; +import Box from "@mui/material/Box"; +import OutlinedInput from "@mui/material/OutlinedInput"; +import InputLabel from "@mui/material/InputLabel"; +import MenuItem from "@mui/material/MenuItem"; +import FormControl from "@mui/material/FormControl"; +import Select, { SelectChangeEvent } from "@mui/material/Select"; +import Chip from "@mui/material/Chip"; + +const ITEM_HEIGHT = 48; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, +}; + +function getStyles(name: string, personName: readonly string[], theme: Theme) { + return { + fontWeight: + personName.indexOf(name) === -1 + ? theme.typography.fontWeightRegular + : theme.typography.fontWeightMedium, + }; +} + +type Props = { + data: string[]; + onChange: (values: string[]) => void; +}; + +export default function Filter({ data, onChange }: Props) { + const theme = useTheme(); + const [items, setItems] = React.useState([]); + + const handleChange = (event: SelectChangeEvent) => { + const { + target: { value }, + } = event; + const values = typeof value === "string" ? value.split(",") : value; + setItems(values); + onChange(values); + }; + + return ( + + Statut + + + ); +} diff --git a/targets/frontend/src/modules/agreements/components/List/Row.tsx b/targets/frontend/src/modules/agreements/components/List/Row.tsx deleted file mode 100644 index 6315c2bec6..0000000000 --- a/targets/frontend/src/modules/agreements/components/List/Row.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import TableCell from "@mui/material/TableCell"; -import TableRow from "@mui/material/TableRow"; - -import { AgreementResult } from "./list.query"; -import { Tooltip } from "@mui/material"; - -import GavelIcon from "@mui/icons-material/Gavel"; -import { useRouter } from "next/router"; - -export const Row = ({ row }: { row: AgreementResult }) => { - const router = useRouter(); - - return ( - <> - { - router.push(`/agreements/${row.id}`); - }} - style={{ cursor: "pointer" }} - hover - > - {row.id} - - {row.isSupported ? ( - - - - ) : undefined} - - {row.shortName} - - - ); -}; diff --git a/targets/frontend/src/modules/agreements/components/List/index.tsx b/targets/frontend/src/modules/agreements/components/List/index.tsx index 8a1e2ef1b2..dca75ce46d 100644 --- a/targets/frontend/src/modules/agreements/components/List/index.tsx +++ b/targets/frontend/src/modules/agreements/components/List/index.tsx @@ -1,103 +1,81 @@ -import { - Button, - FormControlLabel, - FormGroup, - Paper, - Stack, - Switch, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, -} from "@mui/material"; -import { useState } from "react"; +import React, { useState } from "react"; import { useListAgreementQuery } from "./list.query"; -import { Row } from "./Row"; import { useRouter } from "next/router"; +import { EnhancedTable } from "src/components/data"; +import { Tooltip } from "@mui/material"; +import GavelIcon from "@mui/icons-material/Gavel"; +import Filter from "./Filter"; + +type AgreementData = { + id: string; + title: string; + isSupported: boolean; +}; export const AgreementList = (): JSX.Element => { const router = useRouter(); - const [idcc, setIdcc] = useState(); const [keyword, setKeyword] = useState(); - const [isSupported, setSupported] = useState(false); + const [isSupported, setSupported] = useState([true, false]); const { rows } = useListAgreementQuery({ - idcc, keyword, isSupported, }); return ( - - - - { - const value = event.target.value; - setIdcc(value ? `%${value}%` : undefined); - }} - data-testid="agreements-idcc-search" - /> - { - const value = event.target.value; - setKeyword(value ? `%${value}%` : undefined); - }} - data-testid="agreements-keyword-search" - /> - - { - // console.log(`Value: ${value.target.value}`); - setSupported(event.target.checked); - }} - checked={isSupported} - /> - } - labelPlacement="start" - label="Afficher que les supportés" - /> - - - - - - - - - IDCC - - Titre - - - - {rows.map((row) => ( - - ))} - -
-
-
+ /> + } + /> ); }; diff --git a/targets/frontend/src/modules/agreements/components/List/list.query.ts b/targets/frontend/src/modules/agreements/components/List/list.query.ts index 6c6c8382df..77933d907f 100644 --- a/targets/frontend/src/modules/agreements/components/List/list.query.ts +++ b/targets/frontend/src/modules/agreements/components/List/list.query.ts @@ -10,8 +10,7 @@ export const listAgreementsQuery = gql` agreements: agreement_agreements( order_by: { id: asc } where: { - id: { _ilike: $idcc } - name: { _ilike: $keyword } + _or: [{ id: { _ilike: $idcc } }, { shortName: { _ilike: $keyword } }] _and: { id: { _neq: "0000" } } isSupported: { _in: $isSupported } } @@ -33,9 +32,8 @@ export type QueryResult = { }; export type AgreementListQueryProps = { - idcc?: string; keyword?: string; - isSupported: boolean; + isSupported: boolean[]; }; export type AgreementsListQueryResult = { @@ -43,7 +41,6 @@ export type AgreementsListQueryResult = { }; export const useListAgreementQuery = ({ - idcc, keyword, isSupported, }: AgreementListQueryProps): AgreementsListQueryResult => { @@ -51,9 +48,9 @@ export const useListAgreementQuery = ({ query: listAgreementsQuery, requestPolicy: "cache-and-network", variables: { - idcc: idcc?.length ?? 0 > 0 ? `%${idcc}%` : "%", + idcc: keyword?.length ?? 0 > 0 ? `%${keyword}%` : "%", keyword: keyword?.length ?? 0 > 0 ? `%${keyword}%` : "%", - isSupported: isSupported ? [true] : [true, false], + isSupported: isSupported, }, }); return { diff --git a/targets/frontend/src/modules/documents/components/publish.mutation.ts b/targets/frontend/src/modules/documents/components/publish.mutation.ts new file mode 100644 index 0000000000..84d0113b52 --- /dev/null +++ b/targets/frontend/src/modules/documents/components/publish.mutation.ts @@ -0,0 +1,47 @@ +import { OperationResult, useMutation } from "urql"; + +export const publishMutation = ` +mutation publish( + $id: uuid!, + $source: String! +) { + publish(id: $id, source: $source) { + cdtnId + } +} +`; + +export type Source = + | "information" + | "modeles_de_courriers" + | "contributions" + | "conventions_collectives"; + +export type PublishProps = { + id: string; + source: Source; +}; + +type Result = { + cdtnId: string; +}; + +export type PublishMutationResult = ( + props: PublishProps +) => Promise>; + +export const usePublishMutation = (): PublishMutationResult => { + const [, execute] = useMutation(publishMutation); + const resultFunction = async ({ id, source }: PublishProps) => { + const result = await execute({ id, source }); + if (result.error) { + console.error( + `Publication error for ${source} with id ${id}`, + result.error + ); + throw new Error(result.error.message); + } + return result; + }; + return resultFunction; +}; diff --git a/targets/frontend/src/modules/informations/components/List/index.tsx b/targets/frontend/src/modules/informations/components/List/index.tsx new file mode 100644 index 0000000000..049e6aa50b --- /dev/null +++ b/targets/frontend/src/modules/informations/components/List/index.tsx @@ -0,0 +1,42 @@ +import { useState } from "react"; +import { useRouter } from "next/router"; + +import { useInformationsListQuery } from "./list.query"; +import { EnhancedTable } from "src/components/data"; + +type InfoData = { + id: string; + title: string; +}; + +export const InformationList = (): JSX.Element => { + const [search, setSearch] = useState(); + const router = useRouter(); + const { rows } = useInformationsListQuery({ + search, + }); + + return ( + + source="information" + headCells={[ + { + id: "id", + dataIndex: "title", + label: "Titre", + }, + ]} + rows={rows.map((row) => ({ + id: row.id ?? "", + title: row.title, + }))} + onClickItem={(id) => { + router.push(`/informations/${id}`); + }} + onClickCreation={() => { + router.push("/informations/creation"); + }} + setSearch={(value) => setSearch(value)} + /> + ); +}; diff --git a/targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts b/targets/frontend/src/modules/informations/components/List/list.query.ts similarity index 89% rename from targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts rename to targets/frontend/src/modules/informations/components/List/list.query.ts index e7c7a2254b..69a6367c19 100644 --- a/targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts +++ b/targets/frontend/src/modules/informations/components/List/list.query.ts @@ -1,7 +1,7 @@ import { useQuery } from "urql"; import { Information } from "../../type"; -export const informationsListQuery = `query informationsList($search: String) { +export const listQuery = `query informationsList($search: String) { information_informations( where: { title: { _ilike: $search } @@ -39,7 +39,7 @@ export const useInformationsListQuery = ({ search, }: InformationsListQueryProps): InformationsListQueryResult => { const [result] = useQuery({ - query: informationsListQuery, + query: listQuery, requestPolicy: "cache-and-network", variables: { search, diff --git a/targets/frontend/src/modules/informations/components/index.ts b/targets/frontend/src/modules/informations/components/index.ts index 66633c1568..d508de06e0 100644 --- a/targets/frontend/src/modules/informations/components/index.ts +++ b/targets/frontend/src/modules/informations/components/index.ts @@ -1,2 +1,2 @@ -export * from "./informationsList"; +export * from "./List"; export * from "./informationsEdit"; diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsEdit.tsx b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsEdit.tsx index cf2b3d717b..6a3b3ab1e6 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsEdit.tsx +++ b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsEdit.tsx @@ -9,7 +9,7 @@ import { } from "./editInformation.mutation"; import { InformationsForm } from "./InformationsForm"; import { useDeleteInformationMutation } from "./deleteInformation.mutation"; -import { usePublishInformationMutation } from "./publishInformation.mutation"; +import { usePublishMutation } from "../../../documents/components/publish.mutation"; import { useRouter } from "next/router"; import { SnackBar } from "src/components/utils/SnackBar"; import { ConfirmModal } from "src/modules/common/components/modals/ConfirmModal"; @@ -36,7 +36,7 @@ export const InformationsEdit = ({ id }: EditInformationProps): JSX.Element => { const [modalDelete, setModalDelete] = useState(false); const onUpsert = useEditInformationMutation(); const onDelete = useDeleteInformationMutation(); - const onPublish = usePublishInformationMutation(); + const onPublish = usePublishMutation(); useEffect(() => { if (!fetching && information) { @@ -77,7 +77,7 @@ export const InformationsEdit = ({ id }: EditInformationProps): JSX.Element => { }} onUpsert={async (upsertData) => { try { - const idUpsert = await onUpsert(upsertData); + await onUpsert(upsertData); reexecuteQuery({ requestPolicy: "network-only" }); setSnack({ open: true, @@ -95,7 +95,10 @@ export const InformationsEdit = ({ id }: EditInformationProps): JSX.Element => { onPublish={async () => { try { if (information?.id) { - await onPublish(information.id); + await onPublish({ + id: information.id, + source: "information", + }); setSnack({ open: true, severity: "success", diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/publishInformation.mutation.ts b/targets/frontend/src/modules/informations/components/informationsEdit/publishInformation.mutation.ts deleted file mode 100644 index 4fec373614..0000000000 --- a/targets/frontend/src/modules/informations/components/informationsEdit/publishInformation.mutation.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { OperationResult, useMutation } from "urql"; - -export const publishInformationMutation = ` -mutation publish_information( - $id: uuid! -) { - publish(id: $id, source: "information") { - cdtnId - } -} -`; - -export type PublishInformationMutationResult = ( - id: string -) => Promise; - -export const usePublishInformationMutation = - (): PublishInformationMutationResult => { - const [, execute] = useMutation(publishInformationMutation); - const resultFunction = async (id: string) => { - const result = await execute({ id }); - if (result.error) { - throw new Error(result.error.message); - } - return result; - }; - return resultFunction; - }; diff --git a/targets/frontend/src/modules/informations/components/informationsList/InformationsList.tsx b/targets/frontend/src/modules/informations/components/informationsList/InformationsList.tsx deleted file mode 100644 index 3acda19c95..0000000000 --- a/targets/frontend/src/modules/informations/components/informationsList/InformationsList.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { - Paper, - Stack, - Table, - TableBody, - TableHead, - TableContainer, - TextField, - TableRow, - TableCell, - Button, -} from "@mui/material"; -import { useState } from "react"; -import { useRouter } from "next/router"; - -import { useInformationsListQuery } from "./InformationsList.query"; -import { InformationsRow } from "./InformationsRow"; - -export const QuestionList = (): JSX.Element => { - const [search, setSearch] = useState(); - const router = useRouter(); - const { rows } = useInformationsListQuery({ - search, - }); - return ( - - - { - const value = event.target.value; - setSearch(value ? `%${value}%` : undefined); - }} - data-testid="informations-list-search" - /> - - - - - - - Titre - - - - {rows.map((row) => ( - - ))} - -
-
-
- ); -}; diff --git a/targets/frontend/src/modules/informations/components/informationsList/InformationsRow.tsx b/targets/frontend/src/modules/informations/components/informationsList/InformationsRow.tsx deleted file mode 100644 index cc0e3c802f..0000000000 --- a/targets/frontend/src/modules/informations/components/informationsList/InformationsRow.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import TableCell from "@mui/material/TableCell"; -import TableRow from "@mui/material/TableRow"; -import { useRouter } from "next/router"; - -import { QueryInformation } from "./InformationsList.query"; - -export const InformationsRow = (props: { row: QueryInformation }) => { - const { row } = props; - const router = useRouter(); - - return ( - <> - { - router.push(`/informations/${row.id}`); - }} - style={{ cursor: "pointer" }} - hover - > - - {row.title} - - - - ); -}; diff --git a/targets/frontend/src/modules/informations/components/informationsList/index.ts b/targets/frontend/src/modules/informations/components/informationsList/index.ts deleted file mode 100644 index bc7481f522..0000000000 --- a/targets/frontend/src/modules/informations/components/informationsList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./InformationsList"; diff --git a/targets/frontend/src/modules/models/components/Edition/index.tsx b/targets/frontend/src/modules/models/components/Edition/index.tsx index b846aae071..d2c48e9113 100644 --- a/targets/frontend/src/modules/models/components/Edition/index.tsx +++ b/targets/frontend/src/modules/models/components/Edition/index.tsx @@ -4,7 +4,7 @@ import { useListModelQuery } from "./model.query"; import React from "react"; import { ModelForm } from "src/modules/models/components/Common"; import { useModelUpdateMutation } from "./model.mutation"; -import { usePublishMutation } from "./publish.mutation"; +import { usePublishMutation } from "../../../documents/components/publish.mutation"; type Props = { id: string; @@ -61,7 +61,7 @@ export const ModelEdition = ({ id }: Props): React.ReactElement => { }} onPublish={async () => { if (data?.id) { - await publish(data.id); + await publish({ id: data.id, source: "modeles_de_courriers" }); } else { throw new Error( "Aucune modèle de document à publier n'a été détecté" diff --git a/targets/frontend/src/modules/models/components/Edition/publish.mutation.ts b/targets/frontend/src/modules/models/components/Edition/publish.mutation.ts deleted file mode 100644 index 3235797ebd..0000000000 --- a/targets/frontend/src/modules/models/components/Edition/publish.mutation.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { OperationResult, useMutation } from "urql"; - -export const publishMutation = ` -mutation publish_information( - $id: uuid! -) { - publish(id: $id, source: "modeles_de_courriers") { - cdtnId - } -} -`; - -export type PublishMutationResult = (id: string) => Promise; - -export const usePublishMutation = (): PublishMutationResult => { - const [, execute] = useMutation(publishMutation); - const resultFunction = async (id: string) => { - const result = await execute({ id }); - if (result.error) { - throw new Error(result.error.message); - } - return result; - }; - return resultFunction; -}; diff --git a/targets/frontend/src/modules/models/components/List/Row.tsx b/targets/frontend/src/modules/models/components/List/Row.tsx deleted file mode 100644 index 480ba4bbd8..0000000000 --- a/targets/frontend/src/modules/models/components/List/Row.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import TableCell from "@mui/material/TableCell"; -import TableRow from "@mui/material/TableRow"; -import { useRouter } from "next/router"; - -import { ModelResult } from "./list.query"; - -export const Row = (props: { row: ModelResult }) => { - const { row } = props; - const router = useRouter(); - - return ( - <> - { - router.push(`/models/${row.id}`); - }} - style={{ cursor: "pointer" }} - hover - > - - {row.title} - - - - ); -}; diff --git a/targets/frontend/src/modules/models/components/List/index.tsx b/targets/frontend/src/modules/models/components/List/index.tsx index 1ab8e6f795..e7815a9e9d 100644 --- a/targets/frontend/src/modules/models/components/List/index.tsx +++ b/targets/frontend/src/modules/models/components/List/index.tsx @@ -1,20 +1,13 @@ -import { - Button, - Paper, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, -} from "@mui/material"; import { useRouter } from "next/router"; import { useState } from "react"; import { useListModelQuery } from "./list.query"; -import { Row } from "./Row"; +import { EnhancedTable } from "src/components/data"; + +type ModelData = { + id: string; + title: string; +}; export const ModelList = (): JSX.Element => { const [search, setSearch] = useState(); @@ -23,47 +16,26 @@ export const ModelList = (): JSX.Element => { search, }); return ( - - - { - const value = event.target.value; - setSearch(value ? `%${value}%` : undefined); - }} - data-testid="models-list-search" - /> - - - - - - - - Titre - - - - {rows.map((row) => ( - - ))} - -
-
-
+ + source="modeles_de_courriers" + headCells={[ + { + id: "id", + dataIndex: "title", + label: "Titre", + }, + ]} + rows={rows.map((row) => ({ + id: row.id ?? "", + title: row.title, + }))} + onClickItem={(id) => { + router.push(`/models/${id}`); + }} + onClickCreation={() => { + router.push("/models/creation"); + }} + setSearch={(value) => setSearch(value)} + /> ); }; diff --git a/targets/frontend/src/pages/informations/index.tsx b/targets/frontend/src/pages/informations/index.tsx index 93b72f173e..37c9ef0e2f 100644 --- a/targets/frontend/src/pages/informations/index.tsx +++ b/targets/frontend/src/pages/informations/index.tsx @@ -1,11 +1,11 @@ -import { QuestionList } from "src/modules/informations"; +import { InformationList } from "src/modules/informations"; import { Layout } from "src/components/layout/auth.layout"; export function InformationsPage() { return ( - + ); } diff --git a/targets/hasura/metadata/actions.yaml b/targets/hasura/metadata/actions.yaml index 5ff5bc558d..854b5034d2 100644 --- a/targets/hasura/metadata/actions.yaml +++ b/targets/hasura/metadata/actions.yaml @@ -4,6 +4,7 @@ actions: kind: synchronous handler: '{{API_URL}}/actions/publish' forward_client_headers: true + timeout: 180 permissions: - role: super comment: Action pour publier un document