Skip to content

Commit

Permalink
feat: refactor indicateur2et3 (#1885)
Browse files Browse the repository at this point in the history
  • Loading branch information
pom421 authored Nov 28, 2023
1 parent c73f9a1 commit 7a1e870
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import Alert from "@codegouvfr/react-dsfr/Alert";
import Button from "@codegouvfr/react-dsfr/Button";
import Input from "@codegouvfr/react-dsfr/Input";
import { createModal } from "@codegouvfr/react-dsfr/Modal";
import RadioButtons from "@codegouvfr/react-dsfr/RadioButtons";
import { cx } from "@codegouvfr/react-dsfr/tools/cx";
import { IndicateurDeuxTroisComputer } from "@common/core-domain/computers/IndicateurDeuxTroisComputer";
import { IndicateurUnComputer } from "@common/core-domain/computers/IndicateurUnComputer";
import { CompanyWorkforceRange } from "@common/core-domain/domain/valueObjects/declaration/CompanyWorkforceRange";
import {
type CreateSimulationDTO,
type CreateSimulationWorkforceRangeLessThan250DTO,
Expand All @@ -20,24 +18,23 @@ import { percentFormat } from "@common/utils/number";
import { type ClearObject } from "@common/utils/types";
import { storePicker } from "@common/utils/zustand";
import { AideSimulationIndicateurDeuxEtTrois } from "@components/aide-simulation/IndicateurDeuxEtTrois";
import { RadioOuiNon } from "@components/RHF/RadioOuiNon";
import { ClientBodyPortal } from "@components/utils/ClientBodyPortal";
import { SkeletonForm } from "@components/utils/skeleton/SkeletonForm";
import { BackNextButtonsGroup, Container, FormLayout, Grid, GridCol, Text } from "@design-system";
import { ClientAnimate } from "@design-system/utils/client/ClientAnimate";
import { zodResolver } from "@hookform/resolvers/zod";
import { redirect, useRouter } from "next/navigation";
import { useState } from "react";
import { Controller, type FieldErrors, FormProvider, useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { type FieldErrors, FormProvider, useForm } from "react-hook-form";
import { z } from "zod";

import { NAVIGATION, simulateurPath } from "../navigation";
import { useSimuFunnelStore, useSimuFunnelStoreHasHydrated } from "../useSimuFunnelStore";
import { useSimuFunnelStore } from "../useSimuFunnelStore";
import { getTotalsCsp, prepareIndicateurUnComputer } from "../utils";
import style from "./Form.module.scss";
import { Indicateur2et3Note } from "./Indicateur2et3Note";

type Indic2and3FormType = ClearObject<z.infer<typeof createSteps.indicateur2and3>>;
type Indic2and3FormWhenCalculated = Extract<Indic2and3FormType, { calculable: true }>;
type Indic2and3FormWhenCalculated = Extract<Indic2and3FormType, { calculable: "oui" }>;

const indicateur1Computer = new IndicateurUnComputer();
const indicateur2and3Computer = new IndicateurDeuxTroisComputer(indicateur1Computer);
Expand All @@ -60,18 +57,10 @@ const infoModal = createModal({
isOpenedByDefault: false,
});

const useStore = storePicker(useSimuFunnelStore);
export const Indic2and3Form = () => {
const router = useRouter();
const [_funnel, saveFunnel] = useStore("funnel", "saveFunnel");
const funnel = _funnel as Partial<CreateSimulationWorkforceRangeLessThan250DTO> | undefined;
const hydrated = useSimuFunnelStoreHasHydrated();

const [lastRaisedCount, setLastRaisedCount] = useState<Indic2and3FormWhenCalculated["raisedCount"]>();

const schemaWithMax = createSteps.indicateur2and3.superRefine((data, ctx) => {
if (data.calculable && funnel) {
const [totalCspWomen, totalCspMen] = getTotalsCsp(funnel as CreateSimulationDTO);
const schemaWithMax = (totalCsp: [number, number]) =>
createSteps.indicateur2and3.superRefine((data, ctx) => {
if (data.calculable === "oui") {
const [totalCspWomen, totalCspMen] = totalCsp;

if (data.raisedCount.women !== "" && data.raisedCount.women > totalCspWomen) {
ctx.addIssue({
Expand All @@ -97,43 +86,31 @@ export const Indic2and3Form = () => {
}
});

const useStore = storePicker(useSimuFunnelStore);

export const Indic2and3Form = () => {
const router = useRouter();
const [_funnel, saveFunnel] = useStore("funnel", "saveFunnel");
const funnel = _funnel as Partial<CreateSimulationWorkforceRangeLessThan250DTO> | undefined;
const [totalCspWomen, totalCspMen] = getTotalsCsp(funnel as CreateSimulationDTO);
prepareIndicateurUnComputer(indicateur1Computer, funnel as CreateSimulationDTO);

const methods = useForm<Indic2and3FormType>({
mode: "onChange",
resolver: zodResolver(schemaWithMax),
resolver: zodResolver(schemaWithMax([totalCspWomen, totalCspMen])),
defaultValues: funnel?.indicateur2and3,
shouldUnregister: true,
});

const {
formState: { isValid, errors },
handleSubmit,
register,
watch,
getValues,
reset,
trigger,
control,
} = methods;

if (!hydrated) {
return <SkeletonForm fields={2} />;
}

if (!funnel?.effectifs) {
redirect(simulateurPath("effectifs"));
}

if (!funnel.indicateur1) {
redirect(simulateurPath("indicateur1"));
}

if (funnel.effectifs.workforceRange !== CompanyWorkforceRange.Enum.FROM_50_TO_250) {
redirect(simulateurPath("indicateur2"));
}

const computableCheck = watch("calculable");
const [totalCspWomen, totalCspMen] = getTotalsCsp(funnel as CreateSimulationDTO);
prepareIndicateurUnComputer(indicateur1Computer, funnel as CreateSimulationDTO);

const raisedCount = watch("raisedCount");

indicateur2and3Computer.setInput({
Expand All @@ -143,12 +120,12 @@ export const Indic2and3Form = () => {
womenCount: totalCspWomen,
});

let result = {} as IndicateurDeuxTroisComputer.ComputedResult;
let computed = undefined as IndicateurDeuxTroisComputer.ComputedResult | undefined;

const canCompute = indicateur2and3Computer.canCompute();

if (canCompute) {
result = indicateur2and3Computer.compute();
} else {
register("calculable", { value: false });
computed = indicateur2and3Computer.compute();
}

const onSubmit = async (formData: Indic2and3FormType) => {
Expand All @@ -157,6 +134,7 @@ export const Indic2and3Form = () => {
};

const whenCalculableErrors = errors as FieldErrors<Indic2and3FormWhenCalculated>;

return (
<FormProvider {...methods}>
<form noValidate onSubmit={handleSubmit(onSubmit)}>
Expand All @@ -177,58 +155,17 @@ export const Indic2and3Form = () => {
/>
) : (
<>
<Controller
control={control}
<RadioOuiNon
legend="Y a-t-il eu des augmentations individuelles durant la période de référence ?"
name="calculable"
render={({ field, fieldState }) => (
<RadioButtons
orientation="horizontal"
legend="Y a-t-il eu des augmentations individuelles durant la période de référence ?"
state={fieldState.error && "error"}
stateRelatedMessage={fieldState.error?.message}
options={[
{
label: "Oui",
nativeInputProps: {
...field,
value: 1,
defaultChecked: field.value === true,
onChange() {
reset({
calculable: true,
raisedCount: lastRaisedCount,
});
trigger("raisedCount");
setLastRaisedCount(void 0);
field.onChange(true);
},
},
},
{
label: "Non",
nativeInputProps: {
...field,
value: 0,
defaultChecked: field.value === false,
onChange() {
setLastRaisedCount(getValues("raisedCount"));
reset({
calculable: false,
});
field.onChange(false);
},
},
},
]}
/>
)}
triggerValidation={true}
/>
{computableCheck ? (
{computableCheck === "oui" ? (
<>
<Container className={cx(fr.cx("fr-px-md-3v", "fr-px-2v", "fr-mb-6w"), style["form-input-card"])}>
<Grid haveGutters>
<GridCol className={style["form-input-card-title"]}>
<Text text="Nombre de salaries augmentés" inline variant={["xl", "bold"]} mb="auto" />
<Text text="Nombre de salariés augmentés" inline variant={["xl", "bold"]} mb="auto" />
<Button
title="Information indicateur écart de taux d'augmentation"
iconId="fr-icon-information-fill"
Expand All @@ -239,46 +176,64 @@ export const Indic2and3Form = () => {
/>
</GridCol>
<GridCol sm={6}>
<Input
label="Femmes"
hintText={`(max ${totalCspWomen})`}
state={whenCalculableErrors.raisedCount?.women && "error"}
stateRelatedMessage={whenCalculableErrors.raisedCount?.women?.message}
nativeInputProps={{
...register("raisedCount.women", {
setValueAs: setValueAsFloatOrEmptyString,
deps: "raisedCount.men",
}),
type: "number",
min: raisedCount && raisedCount.men !== "" && raisedCount.men > 0 ? 0 : 1,
max: totalCspWomen,
}}
/>
{(() => {
const field = register("raisedCount.women", {
setValueAs: setValueAsFloatOrEmptyString,
});

return (
<Input
label="Femmes"
hintText={`(max ${totalCspWomen})`}
state={whenCalculableErrors.raisedCount?.women && "error"}
stateRelatedMessage={whenCalculableErrors.raisedCount?.women?.message}
nativeInputProps={{
type: "number",
min: raisedCount && raisedCount.men !== "" && raisedCount.men > 0 ? 0 : 1,
max: totalCspWomen,
...field,
onChange: e => {
field.onChange(e);
trigger();
},
}}
/>
);
})()}
</GridCol>
<GridCol sm={6}>
<Input
label="Hommes"
hintText={`(max ${totalCspMen})`}
state={whenCalculableErrors.raisedCount?.men && "error"}
stateRelatedMessage={whenCalculableErrors.raisedCount?.men?.message}
nativeInputProps={{
...register("raisedCount.men", {
setValueAs: setValueAsFloatOrEmptyString,
deps: "raisedCount.women",
}),
type: "number",
min: raisedCount && raisedCount.women !== "" && raisedCount.women > 0 ? 0 : 1,
max: totalCspMen,
}}
/>
{(() => {
const field = register("raisedCount.men", {
setValueAs: setValueAsFloatOrEmptyString,
});

return (
<Input
label="Hommes"
hintText={`(max ${totalCspMen})`}
state={whenCalculableErrors.raisedCount?.men && "error"}
stateRelatedMessage={whenCalculableErrors.raisedCount?.men?.message}
nativeInputProps={{
type: "number",
min: raisedCount && raisedCount.women !== "" && raisedCount.women > 0 ? 0 : 1,
max: totalCspMen,
...field,
onChange: e => {
field.onChange(e);
trigger();
},
}}
/>
);
})()}
</GridCol>
<GridCol sm={12}>
<Text
mb="1w"
text={
<>
Écart en valeur absolue :{" "}
<strong>{percentFormat.format(result?.result / 100 ?? 0)}</strong>
<strong>{computed?.result ? percentFormat.format(computed.result / 100) : ""}</strong>
</>
}
/>
Expand All @@ -290,7 +245,7 @@ export const Indic2and3Form = () => {
<strong>
<sup>*</sup>
</strong>{" "}
: <strong>{result?.equivalentEmployeeCountGap}</strong>
: <strong>{computed?.equivalentEmployeeCountGap}</strong>
</>
}
/>
Expand All @@ -300,7 +255,8 @@ export const Indic2and3Form = () => {
variant={["sm"]}
text={
<>
<strong>*</strong> {ifAdvantageText[result.ifadvantage]}
<strong>*</strong>
{computed?.ifadvantage ? ifAdvantageText[computed.ifadvantage] : ""}
</>
}
/>
Expand All @@ -309,10 +265,19 @@ export const Indic2and3Form = () => {
</Grid>
</Container>

<Indicateur2et3Note computer={indicateur2and3Computer} isValid={isValid} />
{whenCalculableErrors.raisedCount && whenCalculableErrors.raisedCount.root && (
<Alert
className="fr-mb-3w"
severity="error"
title=""
description={whenCalculableErrors.raisedCount.root?.message || ""}
/>
)}

{computed && <Indicateur2et3Note computed={computed} isValid={isValid} />}
</>
) : (
computableCheck === false && (
computableCheck === "non" && (
<Alert
className="fr-mb-3w"
severity="info"
Expand Down
Loading

0 comments on commit 7a1e870

Please sign in to comment.