Skip to content

Commit

Permalink
feat(simulateurV2): recap page (#1717)
Browse files Browse the repository at this point in the history
  • Loading branch information
lsagetlethias authored Aug 16, 2023
1 parent ca86818 commit 72f3720
Show file tree
Hide file tree
Showing 25 changed files with 985 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import RadioButtons from "@codegouvfr/react-dsfr/RadioButtons";
import { type ComputedResult } from "@common/core-domain/computers/AbstractComputer";
import { IndicateurDeuxComputer, type Percentages } from "@common/core-domain/computers/IndicateurDeuxComputer";
import { IndicateurTroisComputer } from "@common/core-domain/computers/IndicateurTroisComputer";
import { ageRanges, categories } from "@common/core-domain/computers/utils";
import { IndicateurUnComputer } from "@common/core-domain/computers/IndicateurUnComputer";
import { categories } from "@common/core-domain/computers/utils";
import { CSP } from "@common/core-domain/domain/valueObjects/CSP";
import { CompanyWorkforceRange } from "@common/core-domain/domain/valueObjects/declaration/CompanyWorkforceRange";
import {
Expand All @@ -14,7 +15,6 @@ import {
createSteps,
} from "@common/core-domain/dtos/CreateSimulationDTO";
import { precisePercentFormat } from "@common/utils/number";
import { Object } from "@common/utils/overload";
import { storePicker } from "@common/utils/zustand";
import { AideSimulationIndicateurDeux } from "@components/aide-simulation/IndicateurDeux";
import { AideSimulationIndicateurTrois } from "@components/aide-simulation/IndicateurTrois";
Expand All @@ -29,42 +29,20 @@ import { type z } from "zod";

import { NAVIGATION, simulateurPath } from "../navigation";
import { useSimuFunnelStore, useSimuFunnelStoreHasHydrated } from "../useSimuFunnelStore";
import { getResultIndicateurUn } from "../utils";
import { getPourcentagesAugmentationPromotionsWithCount, prepareIndicateurUnComputer } from "../utils";
import { Indicateur2ou3Note } from "./Indicateur2ou3Note";

type Indic2or3FormType = z.infer<typeof createSteps.indicateur2>;
type Indic2or3FormTypeWhenCalculable = Extract<Indic2or3FormType, { calculable: true }>;

const indicateur2Computer = new IndicateurDeuxComputer();
const indicateur3Computer = new IndicateurTroisComputer();
const indicateur1Computer = new IndicateurUnComputer();
const indicateur2Computer = new IndicateurDeuxComputer(indicateur1Computer);
const indicateur3Computer = new IndicateurTroisComputer(indicateur1Computer);

interface Indic2or3FormProps {
indicateur: 2 | 3;
}

const getPourcentagesWithCount = (
funnelCsp: CreateSimulationDTO["effectifs"]["csp"],
pourcentages: Percentages | undefined,
) =>
Object.keys(funnelCsp).reduce(
(newPourcentages, category) => ({
...newPourcentages,
[category]: {
menCount: ageRanges.reduce(
(totalCategoryCount, ageRange) => totalCategoryCount + (funnelCsp[category].ageRanges[ageRange].men ?? 0),
0,
),
womenCount: ageRanges.reduce(
(totalCategoryCount, ageRange) => totalCategoryCount + (funnelCsp[category].ageRanges[ageRange].women ?? 0),
0,
),
men: pourcentages?.[category]?.men ?? 0,
women: pourcentages?.[category]?.women ?? 0,
} as Percentages[CSP.Enum],
}),
{} as Percentages,
);

const useStore = storePicker(useSimuFunnelStore);
export const Indic2or3Form = ({ indicateur }: Indic2or3FormProps) => {
const router = useRouter();
Expand Down Expand Up @@ -111,13 +89,16 @@ export const Indic2or3Form = ({ indicateur }: Indic2or3FormProps) => {
redirect(simulateurPath("indicateur2et3"));
}

const resultIndicateurUn = getResultIndicateurUn(funnel as CreateSimulationDTO);
prepareIndicateurUnComputer(indicateur1Computer, funnel as CreateSimulationDTO);

const computableCheck = watch("calculable");
const pourcentages = watch("pourcentages");

// add count from "funnel.effectifs.csp" to pourcentages, to make pourcentagesWithCount
const pourcentagesWithCount = getPourcentagesWithCount(funnel.effectifs.csp, pourcentages as Percentages);
const pourcentagesWithCount = getPourcentagesAugmentationPromotionsWithCount(
funnel.effectifs.csp,
pourcentages as Percentages,
);

computer.setInput(pourcentagesWithCount);

Expand Down Expand Up @@ -314,11 +295,7 @@ export const Indic2or3Form = ({ indicateur }: Indic2or3FormProps) => {
/>

<Box mb="4w">
<Indicateur2ou3Note
computer={computer}
resultIndicateurUn={resultIndicateurUn}
indicateur={indicateur}
/>
<Indicateur2ou3Note computer={computer} indicateur={indicateur} isValid={isValid} />
</Box>
</>
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { fr } from "@codegouvfr/react-dsfr";
import { type ComputedResult } from "@common/core-domain/computers/AbstractComputer";
import { type AbstractGroupComputer } from "@common/core-domain/computers/AbstractGroupComputer";
import { IndicateurUnComputer } from "@common/core-domain/computers/IndicateurUnComputer";
import { RemunerationsMode } from "@common/core-domain/domain/valueObjects/declaration/indicators/RemunerationsMode";
import { type IndicateurDeuxComputer } from "@common/core-domain/computers/IndicateurDeuxComputer";
import { percentFormat } from "@common/utils/number";
import { type Any } from "@common/utils/types";
import { IndicatorNote } from "@design-system";
import { ClientAnimate } from "@design-system/utils/client/ClientAnimate";
import { useFormContext } from "react-hook-form";

interface Props {
computer: AbstractGroupComputer<Any>;
computer: IndicateurDeuxComputer;
indicateur: 2 | 3;
resultIndicateurUn: ComputedResult;
isValid: boolean;
noBorder?: boolean;
simple?: boolean;
}

const augmentations = {
Expand All @@ -33,18 +30,12 @@ const promotions = {
missingDataLegend: "Veuillez remplir le reste des taux de promotions pour avoir votre note",
};

const NOTE_MAX_INDICATEUR1 = new IndicateurUnComputer(RemunerationsMode.Enum.CSP).NOTE_TABLE[0];

export const Indicateur2ou3Note = ({ computer, resultIndicateurUn, indicateur }: Props) => {
const {
formState: { isValid },
} = useFormContext();

const NOTE_MAX = computer.NOTE_TABLE[0];
export const Indicateur2ou3Note = ({ computer, indicateur, isValid, simple, noBorder }: Props) => {
const NOTE_MAX = computer.getMaxNote();

const texts = indicateur === 2 ? augmentations : promotions;

let computed: ComputedResult | null = null;
let computed: IndicateurDeuxComputer.ComputedResult | null = null;
let isNC = false;
let advantageText = "";
try {
Expand All @@ -63,32 +54,31 @@ export const Indicateur2ou3Note = ({ computer, resultIndicateurUn, indicateur }:
// noop
}

const remunerationsCompensated =
computed &&
resultIndicateurUn.note < NOTE_MAX_INDICATEUR1 &&
computed.note < NOTE_MAX &&
resultIndicateurUn.genderAdvantage !== computed.genderAdvantage;

return (
<ClientAnimate>
{isNC ? (
<IndicatorNote
noBorder={noBorder}
note="NC"
size="small"
text={texts.ncText}
legend="Les catégories valides (c’est-à-dire comptant au moins 10 femmes et 10 hommes), représentent moins de 40% des effectifs"
/>
) : (
<>
<IndicatorNote
className={fr.cx("fr-mb-2w")}
size="small"
note={percentFormat.format((computed?.result ?? 0) / 100)}
text={texts.resultText}
legend="Arrondi à la première décimale"
/>
{remunerationsCompensated ? (
{!simple && (
<IndicatorNote
noBorder={noBorder}
className={fr.cx("fr-mb-2w")}
size="small"
note={percentFormat.format((computed?.result ?? 0) / 100)}
text={texts.resultText}
legend="Arrondi à la première décimale"
/>
)}
{computed?.remunerationsCompensated ? (
<IndicatorNote
noBorder={noBorder}
note={NOTE_MAX}
max={NOTE_MAX}
text={texts.balanceText}
Expand All @@ -104,6 +94,7 @@ export const Indicateur2ou3Note = ({ computer, resultIndicateurUn, indicateur }:
/>
) : (
<IndicatorNote
noBorder={noBorder}
note={isValid && computed ? computed.note : "-"}
max={NOTE_MAX}
text={texts.noteText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,32 @@ import {
flattenRemunerations,
} from "@common/core-domain/computers/utils";
import { CSP } from "@common/core-domain/domain/valueObjects/CSP";
import { type RemunerationsMode } from "@common/core-domain/domain/valueObjects/declaration/indicators/RemunerationsMode";
import { CSPAgeRange } from "@common/core-domain/domain/valueObjects/declaration/simulation/CSPAgeRange";
import { type CreateSimulationDTO, type createSteps } from "@common/core-domain/dtos/CreateSimulationDTO";
import { type createSteps } from "@common/core-domain/dtos/CreateSimulationDTO";
import { Object } from "@common/utils/overload";
import { AlternativeTable, type AlternativeTableProps, CenteredContainer } from "@design-system";
import { useFormContext } from "react-hook-form";
import { type z } from "zod";

import { useSimuFunnelStore, useSimuFunnelStoreHasHydrated } from "../useSimuFunnelStore";
import { getCspRemuWithCount } from "../utils";
import { Indicateur1Note } from "./Indicateur1Note";
import { getCommonBodyColumns, getCommonFooter, getCommonHeader } from "./tableUtil";

type Indic1FormType = z.infer<typeof createSteps.indicateur1>;

interface CSPModeTableProps {
computer: IndicateurUnComputer<RemunerationsMode.Enum.CSP>;
computer: IndicateurUnComputer;
staff?: boolean;
}

const getRemuWithCount = (
funnelCsp: CreateSimulationDTO["effectifs"]["csp"],
remunerations: ExternalRemunerations | undefined,
) =>
Object.keys(funnelCsp).map<ExternalRemunerations[number]>(categoryName => ({
name: categoryName,
categoryId: categoryName,
category: ageRanges.reduce(
(newAgeGroups, ageRange) => ({
...newAgeGroups,
[ageRange]: {
womenSalary: remunerations?.find(rem => rem?.name === categoryName)?.category?.[ageRange]?.womenSalary || 0,
menSalary: remunerations?.find(rem => rem?.name === categoryName)?.category?.[ageRange]?.menSalary || 0,
womenCount: funnelCsp[categoryName].ageRanges[ageRange].women,
menCount: funnelCsp[categoryName].ageRanges[ageRange].men,
},
}),
{} as ExternalRemunerations[number]["category"],
),
}));

export const CSPModeTable = ({ computer, staff }: CSPModeTableProps) => {
const funnel = useSimuFunnelStore(state => state.funnel);
const hydrated = useSimuFunnelStoreHasHydrated();

const {
register,
formState: { errors },
formState: { errors, isValid },
watch,
setValue,
trigger,
Expand All @@ -65,7 +44,7 @@ export const CSPModeTable = ({ computer, staff }: CSPModeTableProps) => {
return null;
}

const countOnly = getRemuWithCount(funnel.effectifs.csp, []);
const countOnly = getCspRemuWithCount(funnel.effectifs.csp, []);
computer.setInput(flattenRemunerations(countOnly));
const canCompute = computer.canCompute();
if (!canCompute) {
Expand All @@ -82,7 +61,7 @@ export const CSPModeTable = ({ computer, staff }: CSPModeTableProps) => {
}

const remunerations = watch("remunerations") as ExternalRemunerations;
computer.setInput(flattenRemunerations(getRemuWithCount(funnel.effectifs.csp, remunerations)));
computer.setInput(flattenRemunerations(getCspRemuWithCount(funnel.effectifs.csp, remunerations)));

computer.compute();

Expand Down Expand Up @@ -196,7 +175,7 @@ export const CSPModeTable = ({ computer, staff }: CSPModeTableProps) => {
/>

<CenteredContainer fluid py="1w">
<Indicateur1Note computer={computer} />
<Indicateur1Note computer={computer} isValid={isValid} />
</CenteredContainer>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import { CSPModeTable } from "./CSPModeTable";
import { OtherModesTable } from "./OtherModesTable";
import { getIsEnoughEmployees } from "./tableUtil";

const schemaOtherComputer = new IndicateurUnComputer(RemunerationsMode.Enum.OTHER_LEVEL);
const schemaOtherComputer = new IndicateurUnComputer();
schemaOtherComputer.setMode(RemunerationsMode.Enum.OTHER_LEVEL);
const formSchema = createSteps.indicateur1
.and(createSteps.effectifs)
.superRefine(({ mode, remunerations, csp }, ctx) => {
Expand All @@ -49,8 +50,10 @@ const formSchema = createSteps.indicateur1
type Indic1FormType = z.infer<typeof formSchema>;
const indicateur1Navigation = NAVIGATION.indicateur1;

const cspComputer = new IndicateurUnComputer(RemunerationsMode.Enum.CSP);
const otherComputer = new IndicateurUnComputer(RemunerationsMode.Enum.OTHER_LEVEL);
const cspComputer = new IndicateurUnComputer();
cspComputer.setMode(RemunerationsMode.Enum.CSP);
const otherComputer = new IndicateurUnComputer();
otherComputer.setMode(RemunerationsMode.Enum.OTHER_LEVEL);

const useStore = storePicker(useSimuFunnelStore);
export const Indic1Form = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@ import { fr } from "@codegouvfr/react-dsfr";
import { type ComputedResult } from "@common/core-domain/computers/AbstractComputer";
import { type IndicateurUnComputer } from "@common/core-domain/computers/IndicateurUnComputer";
import { percentFormat } from "@common/utils/number";
import { type Any } from "@common/utils/types";
import { IndicatorNote } from "@design-system";
import { ClientAnimate } from "@design-system/utils/client/ClientAnimate";
import { useFormContext } from "react-hook-form";

interface Props {
computer: IndicateurUnComputer<Any>;
computer: IndicateurUnComputer;
isValid: boolean;
noBorder?: boolean;
simple?: boolean;
}

export const Indicateur1Note = ({ computer }: Props) => {
const {
formState: { isValid },
} = useFormContext();

export const Indicateur1Note = ({ computer, isValid, simple, noBorder }: Props) => {
let computed: ComputedResult | null = null;
let isNC = false;
let advantageText = "";
Expand All @@ -39,21 +36,26 @@ export const Indicateur1Note = ({ computer }: Props) => {
<ClientAnimate>
{isNC ? (
<IndicatorNote
noBorder={noBorder}
note="NC"
size="small"
text="L'indicateur écart de rémunération est non calculable"
legend="L’ensemble des groupes valides (c’est-à-dire comptant au moins 3 femmes et 3 hommes), représentent moins de 40% des effectifs"
/>
) : (
<>
{!simple && (
<IndicatorNote
noBorder={noBorder}
className={fr.cx("fr-mb-2w")}
size="small"
note={percentFormat.format((computed?.result ?? 0) / 100)}
text="Résultat final de l'indicateur écart de rémunération"
legend="Arrondi à la première décimale"
/>
)}
<IndicatorNote
className={fr.cx("fr-mb-2w")}
size="small"
note={percentFormat.format((computed?.result ?? 0) / 100)}
text="Résultat final de l'indicateur écart de rémunération"
legend="Arrondi à la première décimale"
/>
<IndicatorNote
noBorder={noBorder}
note={isValid && computed ? computed.note : "-"}
max={40}
text="Nombre de points obtenus à l'indicateur écart de rémunération"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
type IndicateurUnComputer,
} from "@common/core-domain/computers/IndicateurUnComputer";
import { ageRanges, type ExternalRemunerations, flattenRemunerations } from "@common/core-domain/computers/utils";
import { type RemunerationsMode } from "@common/core-domain/domain/valueObjects/declaration/indicators/RemunerationsMode";
import { CSPAgeRange } from "@common/core-domain/domain/valueObjects/declaration/simulation/CSPAgeRange";
import { type createSteps } from "@common/core-domain/dtos/CreateSimulationDTO";
import { type Any } from "@common/utils/types";
Expand All @@ -22,7 +21,7 @@ import { getCommonBodyColumns, getCommonFooter, getCommonHeader } from "./tableU
type Indic1FormType = z.infer<typeof createSteps.indicateur1>;

interface OtherModesTableProps {
computer: IndicateurUnComputer<RemunerationsMode.Enum.BRANCH_LEVEL | RemunerationsMode.Enum.OTHER_LEVEL>;
computer: IndicateurUnComputer;
defaultRemunerations?: ExternalRemunerations;
staff?: boolean;
}
Expand All @@ -33,7 +32,7 @@ export const OtherModesTable = ({ computer, staff, defaultRemunerations }: Other

const {
register,
formState: { errors },
formState: { errors, isValid },
watch,
control,
} = useFormContext<Indic1FormType>();
Expand Down Expand Up @@ -194,7 +193,7 @@ export const OtherModesTable = ({ computer, staff, defaultRemunerations }: Other
</Button>

<CenteredContainer fluid py="1w">
<Indicateur1Note computer={computer} />
<Indicateur1Note computer={computer} isValid={isValid} />
</CenteredContainer>
</>
);
Expand Down
Loading

0 comments on commit 72f3720

Please sign in to comment.