Skip to content

Commit

Permalink
Merge branch 'main' into POC-bascule
Browse files Browse the repository at this point in the history
  • Loading branch information
C2Chandelier authored Jan 29, 2025
2 parents 42a93e9 + 443e3cb commit 92f9c18
Show file tree
Hide file tree
Showing 64 changed files with 2,367 additions and 187 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { useHistory } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { useToggle } from "react-use";
import { HiOutlineInformationCircle } from "react-icons/hi";

import { AffectationRoutes, CohortDto, TaskName, TaskStatus } from "snu-lib";
import { Button, Tooltip } from "@snu/ds/admin";

import { AffectationService } from "@/services/affectationService";
import AffectationCLESimulationMetropoleModal from "./AffectationCLESimulationMetropoleModal";

interface AffectationCLESimulationMetropoleProps {
session: CohortDto;
}

export default function AffectationCLESimulationMetropole({ session }: AffectationCLESimulationMetropoleProps) {
const history = useHistory();

const [showModal, toggleModal] = useToggle(false);

const {
isPending: isLoading,
isError,
data: affectationStatus,
} = useQuery<AffectationRoutes["GetAffectation"]["response"]>({
queryKey: ["affectation", "cle", session._id], // check SimulationHtsResultStartButton.tsx and AffectationCLESimulationMetropoleModal.tsx queryKey
queryFn: async () => AffectationService.getAffectation(session._id!, "CLE"),
});

const isValidSession = session.type === "CLE";
const isInProgress = affectationStatus && [TaskStatus.IN_PROGRESS, TaskStatus.PENDING].includes(affectationStatus?.simulation?.status as TaskStatus);

return (
<div className="flex items-center justify-between px-4">
<div className="flex gap-2">
<div className="text-sm leading-5 font-bold">Affectation CLE (Metropole)</div>
<Tooltip id="affectation-hts-metropole" title="Affectation CLE (Metropole)">
<HiOutlineInformationCircle className="text-gray-400" size={20} />
</Tooltip>
{isInProgress && <div className="text-xs leading-4 font-normal text-orange-500 italic">Simulation en cours...</div>}
</div>
<div className="flex gap-2">
<Button title="Voir les simulations" type="wired" onClick={() => history.push(`?tab=simulations&cohort=${session.name}&action=${TaskName.AFFECTATION_CLE_SIMULATION}`)} />
<Button title="Lancer une simulation" onClick={toggleModal} loading={isInProgress || isLoading} disabled={!isValidSession || isLoading || isInProgress || isError} />
</div>
{showModal && <AffectationCLESimulationMetropoleModal session={session} onClose={toggleModal} />}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from "react";

import { HiOutlineLightningBolt } from "react-icons/hi";

import { CohortDto, formatDepartement, Phase1Routes, region2department, RegionsMetropole, translate } from "snu-lib";
import { Button, CollapsableSelectSwitcher, Modal, SectionSwitcher } from "@snu/ds/admin";
import { useSetState } from "react-use";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { AffectationService } from "@/services/affectationService";
import { toastr } from "react-redux-toastr";
import { capture } from "@/sentry";

interface AffectationHTSSimulationMetropoleProps {
session: CohortDto;
onClose: () => void;
}

export default function AffectationCLESimulationMetropoleModal({ session, onClose }: AffectationHTSSimulationMetropoleProps) {
const queryClient = useQueryClient();

const [state, setState] = useSetState<{
departements: Record<string, string[]>;
etranger: boolean;
}>({
departements: RegionsMetropole.reduce((acc, region) => {
acc[region] = region2department[region];
return acc;
}, {}),
etranger: true,
});

const isReady = Object.values(state.departements).some((departements) => departements.length > 0);

const { isPending, mutate } = useMutation({
mutationFn: async () => {
return await AffectationService.postSimulationAffectationCLEMetropole(session._id!, {
departements: Object.keys(state.departements).reduce((acc, region) => [...acc, ...state.departements[region]], []),
etranger: state.etranger,
});
},
onSuccess: (task) => {
toastr.success("Le traitement a bien été ajouté", "", { timeOut: 5000 });
const queryKey = ["affectation", "cle", session._id];
const oldStatus = queryClient.getQueryData<Phase1Routes["GetSimulationsRoute"]["response"]>(queryKey) || [];
queryClient.setQueryData(queryKey, { ...oldStatus, simulation: { status: task.status } });
onClose();
},
onError: (error: any) => {
capture(error);
toastr.error("Une erreur est survenue lors de l'ajout du traitement", translate(JSON.parse(error.message).message), { timeOut: 5000 });
},
});

return (
<Modal
isOpen
onClose={onClose}
className="md:max-w-[800px]"
content={
<div className="scroll-y-auto overflow-y-auto max-h-[80vh]">
<div className="flex flex-col items-center text-center gap-6 mb-8">
<div className="flex items-center">
<div className="flex items-center justify-center w-12 h-12 rounded-full bg-gray-50">
<HiOutlineLightningBolt className="w-6 h-6" />
</div>
</div>
<h1 className="font-bold text-xl m-0">Affectation CLE (Metropole)</h1>
</div>
<div className="flex items-start flex-col w-full gap-8">
<div className="flex flex-col w-full gap-2.5">
<h2 className="text-lg leading-7 font-bold m-0">Découpage territorial</h2>
<div className="flex flex-col w-full">
{RegionsMetropole.map((region) => (
<CollapsableSelectSwitcher
key={region}
title={region}
options={region2department[region].map((department) => ({ label: formatDepartement(department), value: department }))}
values={state.departements[region]}
onChange={(values) => setState({ departements: { ...state.departements, [region]: values } })}
isOpen={false}
/>
))}
<SectionSwitcher title="Etranger" value={state.etranger} onChange={(etranger) => setState({ etranger })} className="py-2.5" />
</div>
</div>
</div>
</div>
}
footer={
<div className="flex items-center justify-between gap-6">
<Button title="Annuler" type="secondary" className="flex-1 justify-center" onClick={onClose} />
<Button disabled={!isReady || isPending} onClick={() => mutate()} title="Lancer une simulation" className="flex-1" />
</div>
}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { HiOutlineInformationCircle } from "react-icons/hi";
import { AffectationRoutes, CohortDto, TaskName, TaskStatus } from "snu-lib";
import { Button, Tooltip } from "@snu/ds/admin";

import AffectationSimulationMetropoleModal from "./AffectationSimulationMetropoleModal";
import AffectationHTSSimulationMetropoleModal from "./AffectationHTSSimulationMetropoleModal";
import { AffectationService } from "@/services/affectationService";

interface AffectationSimulationMetropoleProps {
interface AffectationHTSSimulationMetropoleProps {
session: CohortDto;
}

export default function AffectationSimulationMetropole({ session }: AffectationSimulationMetropoleProps) {
export default function AffectationHTSSimulationMetropole({ session }: AffectationHTSSimulationMetropoleProps) {
const history = useHistory();

const [showModal, toggleModal] = useToggle(false);
Expand All @@ -24,26 +24,27 @@ export default function AffectationSimulationMetropole({ session }: AffectationS
isError,
data: affectationStatus,
} = useQuery<AffectationRoutes["GetAffectation"]["response"]>({
queryKey: ["affectation", session._id], // check SimulationHtsResultStartButton.tsx and AffectationSimulationMetropoleModal.tsx queryKey
queryFn: async () => AffectationService.getAffectation(session._id!),
queryKey: ["affectation", "hts", session._id], // check SimulationHtsResultStartButton.tsx and AffectationHTSSimulationMetropoleModal.tsx queryKey
queryFn: async () => AffectationService.getAffectation(session._id!, "HTS"),
});

const isValidSession = session.type === "VOLONTAIRE";
const isInProgress = affectationStatus && [TaskStatus.IN_PROGRESS, TaskStatus.PENDING].includes(affectationStatus?.simulation?.status as TaskStatus);

return (
<div className="flex items-center justify-between p-4">
<div className="flex items-center justify-between px-4">
<div className="flex gap-2">
<div className="text-sm leading-5 font-bold">Affectation HTS (Hors DOM TOM)</div>
<Tooltip id="affectation-hts-metropole" title="Affectation HTS (Hors DOM TOM)">
<div className="text-sm leading-5 font-bold">Affectation HTS (Metropole, hors Corse)</div>
<Tooltip id="affectation-hts-metropole" title="Affectation HTS (Metropole, hors Corse)">
<HiOutlineInformationCircle className="text-gray-400" size={20} />
</Tooltip>
{isInProgress && <div className="text-xs leading-4 font-normal text-orange-500 italic">Simulation en cours...</div>}
</div>
<div className="flex gap-2">
<Button title="Voir les simulations" type="wired" onClick={() => history.push(`?tab=simulations&cohort=${session.name}&action=${TaskName.AFFECTATION_HTS_SIMULATION}`)} />
<Button title="Lancer une simulation" onClick={toggleModal} loading={isInProgress || isLoading} disabled={isLoading || isInProgress || isError} />
<Button title="Lancer une simulation" onClick={toggleModal} loading={isInProgress || isLoading} disabled={!isValidSession || isLoading || isInProgress || isError} />
</div>
{showModal && <AffectationSimulationMetropoleModal session={session} onClose={toggleModal} />}
{showModal && <AffectationHTSSimulationMetropoleModal session={session} onClose={toggleModal} />}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import { toastr } from "react-redux-toastr";
import { capture } from "@/sentry";
import { ReferentielService } from "@/services/ReferentielService";

interface AffectationSimulationMetropoleProps {
interface AffectationHTSSimulationMetropoleProps {
session: CohortDto;
onClose: () => void;
}

export default function AffectationSimulationMetropoleModal({ session, onClose }: AffectationSimulationMetropoleProps) {
export default function AffectationHTSSimulationMetropoleModal({ session, onClose }: AffectationHTSSimulationMetropoleProps) {
const queryClient = useQueryClient();

const [state, setState] = useSetState<{
Expand Down Expand Up @@ -44,7 +44,7 @@ export default function AffectationSimulationMetropoleModal({ session, onClose }

const { isPending, mutate } = useMutation({
mutationFn: async () => {
return await AffectationService.postAffectationMetropole(session._id!, {
return await AffectationService.postSimulationAffectationHTSMetropole(session._id!, {
departements: Object.keys(state.departements).reduce((acc, region) => [...acc, ...state.departements[region]], []),
niveauScolaires: state.niveauScolaires as any,
sdrImportId: sdrImport!.id,
Expand All @@ -54,7 +54,7 @@ export default function AffectationSimulationMetropoleModal({ session, onClose }
},
onSuccess: (task) => {
toastr.success("Le traitement a bien été ajouté", "", { timeOut: 5000 });
const queryKey = ["affectation", session._id];
const queryKey = ["affectation", "hts", session._id];
const oldStatus = queryClient.getQueryData<Phase1Routes["GetSimulationsRoute"]["response"]>(queryKey) || [];
queryClient.setQueryData(queryKey, { ...oldStatus, simulation: { status: task.status } });
onClose();
Expand All @@ -78,7 +78,7 @@ export default function AffectationSimulationMetropoleModal({ session, onClose }
<HiOutlineLightningBolt className="w-6 h-6" />
</div>
</div>
<h1 className="font-bold text-xl m-0">Affectation HTS (Hors DOM TOM)</h1>
<h1 className="font-bold text-xl m-0">Affectation HTS (Metropole, hors Corse)</h1>
<p className="text-lg">
La simulation se basera sur le schéma de répartition suivant : <b>{isLoadingImports ? "..." : sdrImport?.metadata?.parameters?.fileName || "--"}</b>
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Collapsable } from "@snu/ds/admin";

import { CohortDto } from "snu-lib";

import AffectationSimulationMetropole from "./AffectationSimulationMetropole/AffectationSimulationMetropole";
import AffectationHTSSimulationMetropole from "./AffectationSimulation/AffectationHTSSimulationMetropole";
import AffectationCLESimulationMetropole from "./AffectationSimulation/AffectationCLESimulationMetropole";

interface AffectationsSectionProps {
session: CohortDto;
Expand All @@ -12,7 +13,8 @@ interface AffectationsSectionProps {
export default function AffectationsSection({ session }: AffectationsSectionProps) {
return (
<Collapsable title="Affectations">
<AffectationSimulationMetropole session={session} />
<AffectationHTSSimulationMetropole session={session} />
<AffectationCLESimulationMetropole session={session} />
</Collapsable>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import StatusCell from "../components/StatusCell";
import RapportCell from "../components/RapportCell";
import SimulationHtsResultCell from "./affectationHts/SimulationHtsResultCell";
import SimulationHtsResultStartButton from "./affectationHts/SimulationHtsResultStartButton";
import SimulationCleResultCell from "./affectationCle/SimulationCleResultCell";
import SimulationCleResultStartButton from "./affectationCle/SimulationCleResultStartButton";

interface SimulationsSubTabProps {
session: CohortDto;
Expand Down Expand Up @@ -83,8 +85,14 @@ export default function SimulationsSubTab({ session }: SimulationsSubTabProps) {
{
key: "metadata",
title: "Resultats",
// TODO: switch en fonction de Task.name
renderCell: (simulation) => <SimulationHtsResultCell simulation={simulation} />,
renderCell: (simulation) => {
if (simulation.name === TaskName.AFFECTATION_HTS_SIMULATION) {
return <SimulationHtsResultCell simulation={simulation} />;
} else if (simulation.name === TaskName.AFFECTATION_CLE_SIMULATION) {
return <SimulationCleResultCell simulation={simulation} />;
}
return null;
},
},
{
key: "statut",
Expand All @@ -103,6 +111,8 @@ export default function SimulationsSubTab({ session }: SimulationsSubTabProps) {
renderCell: (simulation) => {
if (simulation.name === TaskName.AFFECTATION_HTS_SIMULATION) {
return <SimulationHtsResultStartButton simulation={simulation} />;
} else if (simulation.name === TaskName.AFFECTATION_CLE_SIMULATION) {
return <SimulationCleResultStartButton simulation={simulation} />;
}
return <HiPlay className="text-gray-400" size={50} />;
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

import { SimulationAffectationCLETaskDto } from "snu-lib";

interface SimulationCleResultCellProps {
simulation: unknown;
}

export default function SimulationCleResultCell({ simulation }: SimulationCleResultCellProps) {
const simulationCle = simulation as SimulationAffectationCLETaskDto;

return (
<div className="text-xs leading-4 font-normal">
<div>Classes : {simulationCle.metadata?.results?.classes ?? "--"}</div>
<div>Erreurs : {simulationCle.metadata?.results?.erreurs ?? "--"}</div>
<div>Affectés : {simulationCle.metadata?.results?.jeunesAffected ?? "--"}</div>
</div>
);
}
Loading

0 comments on commit 92f9c18

Please sign in to comment.