diff --git a/targets/frontend/src/components/button/PublishButton.tsx b/targets/frontend/src/components/button/PublishButton.tsx new file mode 100644 index 000000000..b598cd87b --- /dev/null +++ b/targets/frontend/src/components/button/PublishButton.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { Button, CircularProgress as Spinner } from "@mui/material"; + +type Props = { + disabled?: boolean; + isPublishing: boolean; + onClick: () => void; + children: React.ReactNode; +}; + +export function PublishButton({ + disabled=false, + onClick, + children, + isPublishing, +}: Props) { + return isPublishing ? ( + + ) : ( + + ); +} diff --git a/targets/frontend/src/components/contributions/answers/AnswerForm.tsx b/targets/frontend/src/components/contributions/answers/AnswerForm.tsx index 03fb17684..a201e20a9 100644 --- a/targets/frontend/src/components/contributions/answers/AnswerForm.tsx +++ b/targets/frontend/src/components/contributions/answers/AnswerForm.tsx @@ -1,4 +1,4 @@ -import { Button, FormControl, Stack, Alert } from "@mui/material"; +import { Alert, Button, FormControl, Stack } from "@mui/material"; import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -16,6 +16,7 @@ import { } from "./references"; import { getNextStatus, getPrimaryButtonLabel } from "../status/utils"; import { FicheSpDocumentInput } from "./references/FicheSpDocumentInput"; +import { PublishButton } from "src/components/button/PublishButton"; const answerFormBaseSchema = answerRelationSchema .pick({ @@ -315,36 +316,32 @@ export const AnswerForm = ({ : undefined } /> - {!submitting && ( - - - + + {primaryButtonLabel && ( + submit(getNextStatus(status))} + disabled={submitting || status === "TO_PUBLISH"} + isPublishing={submitting} > - Sauvegarder - - {primaryButtonLabel && ( - - )} - - )} + {primaryButtonLabel} + + )} + ); diff --git a/targets/frontend/src/components/contributions/questions/EditQuestionAnswerList.tsx b/targets/frontend/src/components/contributions/questions/EditQuestionAnswerList.tsx index a313e6a44..a7f040cb2 100644 --- a/targets/frontend/src/components/contributions/questions/EditQuestionAnswerList.tsx +++ b/targets/frontend/src/components/contributions/questions/EditQuestionAnswerList.tsx @@ -1,5 +1,4 @@ import { - Button, Checkbox, Paper, Stack, @@ -19,6 +18,7 @@ import { Answer } from "../type"; import { StatusContainer } from "../status"; import { useRouter } from "next/router"; import { fr } from "@codegouvfr/react-dsfr"; +import { PublishButton } from "../../button/PublishButton"; import { StatusPublicationContainer } from "../status/StatusPublication"; type EditQuestionAnswerListProps = { @@ -77,8 +77,10 @@ export const QuestionAnswerList = ({ ) ); const [displayPublish, setDisplayPublish] = useState(false); + const [isPublishing, setIsPublishing] = useState(false); const publishAll = async () => { + setIsPublishing(true); const ids = Object.entries(answersCheck).reduce( (arr, [id, checked]) => { if (checked) { @@ -89,7 +91,12 @@ export const QuestionAnswerList = ({ [] ); const promises = ids.map((id) => onPublish(id)); - await Promise.all(promises); + try { + await Promise.all(promises).finally(() => setIsPublishing(false)); + } catch (e) { + setIsPublishing(false); + throw e; + } }; const redirectToAnswer = (id: string) => { router.push(`/contributions/answers/${id}`); @@ -104,15 +111,13 @@ export const QuestionAnswerList = ({ - + diff --git a/targets/frontend/src/components/documents/Actions.js b/targets/frontend/src/components/documents/Actions.js index 409a9e559..0ffb13539 100644 --- a/targets/frontend/src/components/documents/Actions.js +++ b/targets/frontend/src/components/documents/Actions.js @@ -42,13 +42,12 @@ export function DocumentsListActions({ onUpdatePublication }) { ); diff --git a/targets/frontend/src/components/documents/Container.js b/targets/frontend/src/components/documents/Container.js index 23470e40c..c8ebf5703 100644 --- a/targets/frontend/src/components/documents/Container.js +++ b/targets/frontend/src/components/documents/Container.js @@ -100,8 +100,8 @@ export function DocumentListContainer({ initialFilterValues }) { <>chargement... ) : data.documents.length ? ( - + >; @@ -86,6 +83,7 @@ export const AgreementForm = ({ }); } }; + const [isPublishing, setIsPublishing] = useState(false); return ( @@ -199,11 +197,10 @@ export const AgreementForm = ({ {agreement ? "Sauvegarder" : "Créer"} {onPublish && ( - + )} diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx index ac2370ae7..b51bc042a 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx +++ b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx @@ -1,14 +1,15 @@ -import { Stack, Button, FormControl, Typography } from "@mui/material"; -import React from "react"; -import { useForm, useFieldArray } from "react-hook-form"; +import { Button, FormControl, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { FormTextField, FormRadioGroup } from "src/components/forms"; +import { FormRadioGroup, FormTextField } from "src/components/forms"; import { InformationsResult } from "./Informations.query"; import { Information, informationSchema } from "../../type"; import { InformationsContent } from "./InformationsContent"; import { InformationsReference } from "./InformationsReference"; import { FormCheckbox } from "src/components/forms/Checkbox"; +import { PublishButton } from "../../../../components/button/PublishButton"; export type InformationsFormProps = { data?: InformationsResult; @@ -23,12 +24,7 @@ export const InformationsForm = ({ onDelete, onPublish, }: InformationsFormProps): JSX.Element => { - const { - control, - handleSubmit, - trigger, - formState: { errors }, - } = useForm({ + const { control, handleSubmit } = useForm({ defaultValues: data ?? { title: "", dismissalProcess: false }, resolver: zodResolver(informationSchema), shouldFocusError: true, @@ -56,6 +52,7 @@ export const InformationsForm = ({ const [expandedContent, setExpandedContent] = React.useState( false ); + const [isPublishing, setIsPublishing] = useState(false); return ( <> @@ -222,19 +219,21 @@ export const InformationsForm = ({ Supprimer - + {onPublish && ( + { + if (data?.id) { + setIsPublishing(true); + await onPublish(); + setIsPublishing(false); + } + }} + isPublishing={isPublishing} + > + Publier + + )} diff --git a/targets/frontend/src/modules/models/components/Common/Form.tsx b/targets/frontend/src/modules/models/components/Common/Form.tsx index abde47b4f..cde82aeee 100644 --- a/targets/frontend/src/modules/models/components/Common/Form.tsx +++ b/targets/frontend/src/modules/models/components/Common/Form.tsx @@ -26,6 +26,7 @@ import { LegiReferenceInput } from "src/components/contributions/answers/referen import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { FormOtherReferences } from "../../../../components/forms/OtherReferences"; +import { PublishButton } from "../../../../components/button/PublishButton"; type FormData = Partial>; @@ -144,6 +145,7 @@ export const ModelForm = ({ }); } }; + const [isPublishing, setIsPublishing] = useState(false); const convertToHTML = async (file: File) => { setIsLoadingPreview(true); @@ -300,31 +302,31 @@ export const ModelForm = ({ {model ? "Sauvegarder" : "Créer"} {onPublish && ( - + )} diff --git a/targets/frontend/src/pages/fichiers.tsx b/targets/frontend/src/pages/fichiers.tsx index 69d6bce9a..777466b9f 100644 --- a/targets/frontend/src/pages/fichiers.tsx +++ b/targets/frontend/src/pages/fichiers.tsx @@ -1,4 +1,18 @@ -import { Button, IconButton, ListItem } from "@mui/material"; +import { + Alert, + Box, + Button, + Card, + CircularProgress as Spinner, + IconButton, + InputLabel as Label, + List, + ListItem, + MenuItem, + Select, + TextField as Field, + Stack, +} from "@mui/material"; import prettyBytes from "pretty-bytes"; import { useEffect, useRef, useState } from "react"; import { @@ -12,20 +26,10 @@ import { DropZone } from "src/components/storage/DropZone"; import { useDebouncedState } from "src/hooks/"; import { timeSince } from "src/lib/duration"; import { request } from "src/lib/request"; -import useSWR, { mutate } from "swr"; +import useSWR from "swr"; import { theme } from "../theme"; -import { - Box, - Card, - TextField as Field, - InputLabel as Label, - Alert, - Select, - CircularProgress as Spinner, - MenuItem, - List, -} from "@mui/material"; import { S3File } from "src/lib/upload"; + function FilesPage() { const { data, error, isValidating, mutate } = useSWR("files", () => request(`/api/storage`) @@ -174,7 +178,7 @@ function FilesPage() { {isSearching || (isValidating && !data) ? ( ) : data && data?.length > 0 ? ( - <> + {data .filter( @@ -272,9 +276,9 @@ function FilesPage() { ); })} - + ) : ( - <>Pas de résultats +

Pas de résultats

)} ); diff --git a/targets/frontend/src/pages/themes/edit/[id].js b/targets/frontend/src/pages/themes/edit/[id].js index 547501241..3825da814 100644 --- a/targets/frontend/src/pages/themes/edit/[id].js +++ b/targets/frontend/src/pages/themes/edit/[id].js @@ -126,10 +126,8 @@ export function EditThemePage() { }); } - // const notFound = !fetching && !deleteResult.fetching && !theme?.cdtnId; - return ( - + {fetching ? (