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: contribution zod #1056

Merged
merged 39 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
64ab3e6
feat: implementation du schéma de donnée page info
Sep 6, 2023
56754f4
feat: implementation des pages info
Sep 7, 2023
4727548
Merge branch 'master' into 1007-admin-liste-des-contenus-modifiés-lor…
Sep 7, 2023
e528d27
fix: review
Sep 8, 2023
a0c7f2c
refactor: implement page info refactor
Sep 19, 2023
c9bd5d1
feat: implementation page creation
Sep 20, 2023
bf9759f
feat: add upsert + publish
Sep 22, 2023
f6fd4f2
feat: publish
Sep 25, 2023
db42d1a
feat: publish + refactor to module
Sep 26, 2023
99fec47
Merge branch 'master' into 1007-admin-liste-des-contenus-modifiés-lor…
Sep 26, 2023
999b06d
chore: remove npmrc
Sep 26, 2023
c147530
fix: publish dep
Sep 26, 2023
8d89913
feat: page info validation (#1040)
Viczei Sep 28, 2023
51c4b3b
chore: clean
Sep 28, 2023
0773a21
chore: clean
Sep 28, 2023
1eba143
chore: use helper text for error
Sep 28, 2023
b3cb31f
feat: change url to regex check for files
Sep 28, 2023
ebbab9f
chore: add test
Oct 2, 2023
4779047
chore: review
Oct 2, 2023
e0d3642
Merge branch 'master' into 1007-admin-liste-des-contenus-modifiés-lor…
Oct 2, 2023
be43588
chore: yarn
Oct 2, 2023
649035c
Merge branch 'master' into 1007-admin-liste-des-contenus-modifiés-lor…
Oct 2, 2023
769df7c
refactor: use confirm modal
Oct 2, 2023
fda61bd
chore: downgrade next
Oct 2, 2023
afd4c05
feat: ajout des validations au formulaire contribution
Oct 3, 2023
8822736
feat: gérer le cas des fiches SP
Oct 4, 2023
11ff828
chore: clean
Oct 4, 2023
01e1ded
feat: contrib answer CC hover + CC search + delete question
Oct 4, 2023
50ed2fa
Revert "feat: contrib answer CC hover + CC search + delete question"
Oct 4, 2023
c1afd78
chore: add TU
Oct 5, 2023
fd0ecab
chore: add TU
Oct 5, 2023
eb8c27a
Merge branch 'master' into feat/contribution-zod
Oct 6, 2023
13db745
chore: review
Oct 6, 2023
d471a06
Merge branch 'master' into feat/contribution-zod
Oct 6, 2023
96fc199
chore: review
Oct 6, 2023
916c6b2
chore: review
Oct 6, 2023
a83570a
chore: review
Oct 6, 2023
10864fa
feat: contribution cc hover cc search delete question (#1057)
Viczei Oct 9, 2023
3ac2d7a
fix: url can be empty
m-maillot Oct 9, 2023
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
239 changes: 43 additions & 196 deletions targets/frontend/src/components/contributions/answers/Answer.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,36 @@
import {
AlertColor,
Box,
Button,
FormControl,
Stack,
Tooltip,
TooltipProps,
Typography,
styled,
tooltipClasses,
} from "@mui/material";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import React, { useState } from "react";
import { useUser } from "src/hooks/useUser";

import { FormEditionField, FormRadioGroup, FormTextField } from "../../forms";
import { StatusContainer } from "../status";
import { Answer, Status } from "../type";
import { useContributionAnswerUpdateMutation } from "./answer.mutation";
import { useContributionAnswerQuery } from "./answer.query";
import { Comments } from "./Comments";
import {
CdtnReferenceInput,
KaliReferenceInput,
LegiReferenceInput,
OtherReferenceInput,
} from "./references";
import { statusesMapping } from "../status/data";
import { getNextStatus, getPrimaryButtonLabel } from "../status/utils";
import { SnackBar } from "../../utils/SnackBar";
import { Breadcrumb, BreadcrumbLink } from "src/components/utils";
import { FicheSpDocumentInput } from "./references/FicheSpDocumentInput";
import { AnswerForm } from "./AnswerForm";
import { fr } from "@codegouvfr/react-dsfr";

export type ContributionsAnswerProps = {
id: string;
};

const isNotEditable = (answer: Answer | undefined) =>
answer?.status.status !== "REDACTING" &&
answer?.status.status !== "TODO" &&
answer?.status.status !== "VALIDATING";

const isCodeDuTravail = (answer: Answer): boolean =>
answer.agreement.id === "0000";

export const ContributionsAnswer = ({
id,
}: ContributionsAnswerProps): JSX.Element => {
const answer = useContributionAnswerQuery({ id });
const { user } = useUser() as any;
const [status, setStatus] = useState<Status>("TODO");
useEffect(() => {
if (answer?.status) {
setStatus(answer.status.status);
}
}, [answer]);
const { control, getValues, trigger } = useForm<Answer>({
values: answer,
defaultValues: {
content: "",
contentType: "ANSWER",
status: {
status: "TODO",
},
legiReferences: [],
kaliReferences: [],
otherReferences: [],
cdtnReferences: [],
contentFichesSpDocument: answer?.contentFichesSpDocument ? {} : undefined,
},
});
const updateAnswer = useContributionAnswerUpdateMutation();
const [snack, setSnack] = useState<{
open: boolean;
Expand All @@ -75,19 +40,7 @@ export const ContributionsAnswer = ({
open: false,
});

const onSubmit = async (newStatus: Status) => {
const isValid = await trigger();
if (!isValid) {
return setSnack({
open: true,
severity: "error",
message: "Formulaire invalide",
});
}

setStatus(newStatus);
const data = getValues();

const onSubmit = async (newStatus: Status, data: Answer) => {
try {
if (!answer || !answer.id) {
throw new Error("Id non définit");
Expand All @@ -114,38 +67,12 @@ export const ContributionsAnswer = ({
setSnack({ open: true, severity: "error", message: e.message });
}
};

const agreementResponseOptions = [
{
label: "La convention collective ne prévoit rien",
value: "NOTHING",
},
{
label: "La convention collective renvoie au Code du Travail",
value: "CDT",
},
{
label:
"La convention collective intégralement moins favorable que le CDT",
value: "UNFAVOURABLE",
},
{
label: "Nous n'avons pas la réponse",
value: "UNKNOWN",
},
];
const genericResponseOptions = [
{
label: "Utiliser la fiche service public",
value: "SP",
},
];
return (
<>
<Stack direction="row" justifyContent="space-between">
<Breadcrumb>
<BreadcrumbLink
href={`/contributions/questions/${answer?.question.id}`}
href={`/contributions/questions/${answer?.question?.id}`}
>
<>
<Typography
Expand All @@ -160,136 +87,56 @@ export const ContributionsAnswer = ({
{answer?.question?.content}
</>
</BreadcrumbLink>
<BreadcrumbLink>{answer?.agreement?.id}</BreadcrumbLink>
<BreadcrumbLink>
<HtmlTooltip
title={
<>
<Typography color="inherit">
{answer?.agreement?.name}
</Typography>
</>
}
>
<Typography>{answer?.agreement?.id}</Typography>
</HtmlTooltip>
</BreadcrumbLink>
</Breadcrumb>
{answer?.status && (
<div style={{ color: statusesMapping[status].color }}>
<div style={{ color: statusesMapping[answer?.status.status].color }}>
<StatusContainer status={answer.status} />
</div>
)}
</Stack>
<Box sx={{ display: "flex", flexDirection: "row" }}>
<Stack direction="row">
<Box sx={{ width: "70%" }}>
<form
onSubmit={(e) => {
// This is a hack to prevent the form from being submitted by the tiptap editor.
// The details extension is not working properly and submit the form when click on the arrow.
// See https://github.com/ueberdosis/tiptap/issues/4384
e.preventDefault();
}}
>
<Stack spacing={5}>
<FormControl>
<FormTextField
name="updateDate"
control={control}
label="Date mise à jour"
disabled
labelFixed
/>
</FormControl>
<FormControl>
<FormEditionField
label="Réponse"
name="content"
disabled={isNotEditable(answer)}
control={control}
/>
</FormControl>
{answer && (
<FormRadioGroup
name="contentType"
label="Type de réponse"
control={control}
disabled={isNotEditable(answer)}
options={[
{
label: "Afficher la réponse",
value: "ANSWER",
},
...(isCodeDuTravail(answer)
? genericResponseOptions
: agreementResponseOptions),
]}
/>
)}
{answer && isCodeDuTravail(answer) && (
<FicheSpDocumentInput
name="contentFichesSpDocument"
control={control}
disabled={isNotEditable(answer)}
/>
)}
{answer && !isCodeDuTravail(answer) && (
<KaliReferenceInput
name="kaliReferences"
agreement={answer.agreement}
control={control}
disabled={isNotEditable(answer)}
/>
)}
<LegiReferenceInput
name="legiReferences"
control={control}
disabled={isNotEditable(answer)}
/>
<OtherReferenceInput
name="otherReferences"
control={control}
disabled={isNotEditable(answer)}
/>
<CdtnReferenceInput
name="cdtnReferences"
control={control}
disabled={isNotEditable(answer)}
/>
<Stack
direction="row"
justifyContent="end"
spacing={2}
padding={2}
>
<Button
variant="outlined"
type="button"
onClick={() => onSubmit("REDACTING")}
disabled={status === "TODO" || status === "REDACTING"}
>
Remettre en rédaction
</Button>
<Button
variant="text"
type="button"
disabled={isNotEditable(answer)}
onClick={() => onSubmit("REDACTING")}
>
Sauvegarder
</Button>
<Button
variant="contained"
type="button"
color="success"
onClick={() => onSubmit(getNextStatus(status))}
disabled={status === "PUBLISHED"}
>
{getPrimaryButtonLabel(status)}
</Button>
</Stack>
</Stack>

<SnackBar snack={snack} setSnack={setSnack}></SnackBar>
</form>
{answer && (
<AnswerForm answer={answer} onSubmit={onSubmit}></AnswerForm>
)}
</Box>
<Box sx={{ width: "30%" }}>
{answer && (
{answer && answer.statuses && (
<Comments
answerId={answer.id}
comments={answer.answerComments ?? []}
statuses={answer.statuses}
/>
)}
</Box>
</Box>
</Stack>

<SnackBar snack={snack} setSnack={setSnack}></SnackBar>
</>
);
};

const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: fr.colors.decisions.background.default.grey.hover,
color: fr.colors.decisions.text.default.grey.default,
maxWidth: 220,
fontSize: theme.typography.pxToRem(12),
border: `1px solid ${fr.colors.decisions.border.default.grey.default}`,
},
}));
Loading