Skip to content

Commit

Permalink
retours and amélio
Browse files Browse the repository at this point in the history
  • Loading branch information
nicobret committed Nov 18, 2024
1 parent 5c41c3b commit 3926d83
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 65 deletions.
51 changes: 51 additions & 0 deletions admin/src/scenes/settings/components/CohortGroupSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";
import CreatableSelect from "react-select/creatable";
import useCreateCohortGroup from "../lib/useCreateCohortGroup";
import useCohortGroups from "../lib/useCohortGroups";
import { CohortDto } from "snu-lib";

type propType = {
cohort: CohortDto;
setCohort: (cohort: CohortDto) => void;
readOnly: boolean;
};

type optionType = { value: string; label: string };

export default function CohortGroupSelector({ cohort, setCohort, readOnly }: propType) {
const { isLoading, data } = useCohortGroups();
const options: optionType[] = data?.map((group) => ({ value: group._id, label: group.name })) || [];
const { mutate } = useCreateCohortGroup(cohort);

const handleChange = (selected: optionType) => {
if (selected) {
setCohort({ ...cohort, cohortGroupId: selected.value });
}
};

const handleCreate = async (name: string) => {
if (!window.confirm(`Voulez-vous créer le groupe de cohorte ${name} ?`)) return;
mutate(name, {
onSuccess: (data) => setCohort({ ...cohort, cohortGroupId: data._id }),
});
};

return (
<CreatableSelect
options={options}
value={options?.find((o) => o.value === cohort.cohortGroupId)}
isDisabled={isLoading || readOnly}
onChange={handleChange}
formatCreateLabel={(input) => `Créer le groupe "${input}"`}
onCreateOption={handleCreate}
placeholder="Sélectionnez un groupe de cohorte"
styles={{
control: (base) => ({
...base,
padding: "10px",
minHeight: "40px",
}),
}}
/>
);
}
8 changes: 6 additions & 2 deletions admin/src/scenes/settings/lib/useCohortGroups.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useQuery } from "@tanstack/react-query";
import { getCohortGroups } from "./cohortGroupService";
import { getCohortGroups } from "./cohortGroupRepository";

export default function useCohortGroups() {
return useQuery({ queryFn: getCohortGroups, queryKey: ["cohortGroups"] });
return useQuery({
queryFn: getCohortGroups,
queryKey: ["cohortGroups"],
meta: { errorMessage: "Impossible de charger les groupes de cohortes." },
});
}
4 changes: 3 additions & 1 deletion admin/src/scenes/settings/lib/useCreateCohortGroup.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { queryClient } from "@/services/react-query";
import { useMutation } from "@tanstack/react-query";
import { CohortDto, CohortGroupType } from "snu-lib";
import { createCohortGroup } from "./cohortGroupService";
import { createCohortGroup } from "./cohortGroupRepository";
import { toastr } from "react-redux-toastr";

export default function useCreateCohortGroup(cohort: CohortDto) {
return useMutation({
mutationFn: async (name: string) => await createCohortGroup(name, cohort),
onSuccess: (data) => queryClient.setQueryData(["cohortGroups"], (old: CohortGroupType[]) => [...old, data]),
onError: () => toastr.error("Erreur", "Impossible de créer le groupe de cohortes."),
});
}
45 changes: 5 additions & 40 deletions admin/src/scenes/settings/tabs/GeneralTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ import ToggleDate from "@/components/ui/forms/dateForm/ToggleDate";
import InputText from "@/components/ui/forms/InputText";
import InputTextarea from "@/components/ui/forms/InputTextarea";
import NumberInput from "@/components/ui/forms/NumberInput";
import CreatableSelect from "react-select/creatable";

import { CleSettings } from "../components/CleSettings";
import { InformationsConvoyage } from "../components/InformationsConvoyage";
import { ManualInscriptionSettings } from "../phase0/ManualInscriptionSettings";
import { Select } from "@snu/ds/admin";
import useCohortGroups from "../lib/useCohortGroups";
import useCreateCohortGroup from "../lib/useCreateCohortGroup";
import CohortGroupSelector from "../components/CohortGroupSelector";

// Define the interface for GeneralTab props
interface GeneralTabProps {
Expand All @@ -47,17 +45,6 @@ export default function GeneralTab({ cohort, onCohortChange, readOnly, getCohort
{ value: COHORT_STATUS.ARCHIVED, label: "Archivée" },
];

const { isLoading: isCohortGroupLoading, data: cohortGroups } = useCohortGroups();
const cohortGroupOptions = cohortGroups?.map((group) => ({ value: group._id, label: group.name }));
const { mutate } = useCreateCohortGroup(cohort);

const createCohortGroup = async (name: string) => {
if (!window.confirm(`Voulez-vous créer le groupe de cohorte ${name} ?`)) return;
mutate(name, {
onSuccess: (data) => onCohortChange({ ...cohort, cohortGroupId: data._id }),
});
};

const onSubmit = async () => {
try {
const err = {} as { [key: string]: string };
Expand Down Expand Up @@ -146,35 +133,13 @@ export default function GeneralTab({ cohort, onCohortChange, readOnly, getCohort
<MdInfoOutline data-tip data-for="statut" className="h-5 w-5 cursor-pointer text-gray-400" />
<ReactTooltip id="statut" type="light" place="top" effect="solid" className="custom-tooltip-radius !opacity-100 !shadow-md">
<ul className="w-[275px] list-outside !px-2 !py-1.5 text-left text-xs text-gray-600">
<li>Permet de rattacher une cohorte à un groupement de cohorte pour que les jeunes puissent facilement changer de séjour.</li>
<li className="mt-2">Le jeunes n'auront pas besoin de se réinscirent pour accèder aux cohortes appartenant aux mêmes groupe de cohorte</li>
<li className="mt-2">Permet aussi aux jeunes d'être sûr de mettre à jour leurs informations lors de la reinscription</li>
<li>Permet de rattacher une cohorte à un groupe afin d'orienter les volontaires lors des changements de séjour.</li>
<li className="mt-2">Pour changer de séjour dans un même groupe de cohorte, pas besoin de réinscription.</li>
<li className="mt-2">Pour changer vers un séjour du groupe suivant, réinscription obligatoire.</li>
</ul>
</ReactTooltip>
</div>

<div>
<CreatableSelect
options={cohortGroupOptions}
value={cohortGroupOptions?.find((o) => o.value === cohort.cohortGroupId)}
isDisabled={isLoading || isCohortGroupLoading || readOnly}
onChange={(selected) => {
if (selected) {
onCohortChange({ ...cohort, cohortGroupId: selected.value });
}
}}
formatCreateLabel={(input) => `Créer le groupe "${input}"`}
onCreateOption={createCohortGroup}
placeholder="Sélectionnez un groupe de cohorte"
styles={{
control: (base) => ({
...base,
padding: "10px",
minHeight: "40px",
}),
}}
/>
</div>
<CohortGroupSelector cohort={cohort} setCohort={onCohortChange} readOnly={readOnly} />
</div>
</div>
<div className="flex flex-col gap-3">
Expand Down
11 changes: 10 additions & 1 deletion admin/src/services/react-query.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { capture } from "@/sentry";
import { QueryCache, QueryClient } from "@tanstack/react-query";
import { toastr } from "react-redux-toastr";

export const queryClient = new QueryClient({
queryCache: new QueryCache({ onError: (error) => capture(error) }),
queryCache: new QueryCache({
onError: (error, query) => {
// https://tkdodo.eu/blog/breaking-react-querys-api-on-purpose#defining-on-demand-messages
if (query.meta?.errorMessage) {
toastr.error("Erreur", query.meta.errorMessage);
}
capture(error);
},
}),
});
66 changes: 45 additions & 21 deletions api/migrations/20241105100553-add-cohort-groups.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CohortGroupModel, CohortModel } from "../../../../src/models";
import { logger } from "../src/logger";
import { CohortGroupModel, CohortModel } from "../src/models";
import { COHORT_TYPE } from "snu-lib";

const pipeline = [
Expand Down Expand Up @@ -27,41 +28,64 @@ const pipeline = [

const fromUser = { firstName: "Migration ajout des groupes de cohortes" };

function formatGroupName(type, year) {
return `${type === COHORT_TYPE.VOLONTAIRE ? "HTS" : "CLE"} ${year}`;
}

module.exports = {
async up() {
try {
const groups = await CohortModel.aggregate(pipeline);

if (!groups.length) {
logger.info("No group to migrate");
return;
}

logger.info(`Groupes de cohorte à créer : ${groups.length} + groupe de réserve.`);

const reserveGroup = new CohortGroupModel({ name: "Réserve" });
await reserveGroup.save({ fromUser });

const groups = await CohortModel.aggregate(pipeline);
const groupMap = new Map();

for (const group of groups) {
const type = group.type;
const year = group.year;
const name = `${type === COHORT_TYPE.VOLONTAIRE ? "HTS" : "CLE"} ${year}`;
const groupInserts = groups.map((group) => {
const { type, year } = group;
const name = formatGroupName(type, year);
const newGroup = new CohortGroupModel({ type, year, name });
await newGroup.save({ fromUser });
groupMap.set(name, newGroup._id);
return newGroup.save({ fromUser });
});

for (const doc of group.documents) {
if (!doc._id) continue;
const cohort = await CohortModel.findById(doc._id);
if (!cohort) throw new Error(`Cohort not found: ${doc._id}`);
const insertedGroups = await Promise.all(groupInserts);
logger.info(`Insertion des nouveaux groupes terminée. Nombre de groupes insérés: ${insertedGroups.length}`);

if (cohort.name === "à venir") {
cohort.set({ cohortGroupId: reserveGroup._id });
} else {
cohort.set({ cohortGroupId: newGroup._id });
}
await cohort.save({ fromUser });
const cohorts = await CohortModel.find();

const cohortUpdates = cohorts.map((cohort) => {
const groupName = formatGroupName(cohort.type, new Date(cohort.dateStart).getFullYear());
const groupId = cohort.name === "à venir" ? reserveGroup._id : groupMap.get(groupName);
if (!groupId) {
throw new Error(`Group not found: ${groupName}`);
}
}
return cohort.set({ cohortGroupId: groupId }).save({ fromUser });
});

const updatedCohorts = await Promise.all(cohortUpdates);
logger.info(`Mise à jour des cohortes terminée avec succès. Nombre de cohortes mises à jour: ${updatedCohorts.length}`);
} catch (error) {
console.error(error);
logger.error(error);
throw error;
}
},

async down() {
await CohortGroupModel.deleteMany({});
await CohortModel.updateMany({ cohortGroupId: { $exists: true } }, { $unset: { cohortGroupId: "" } });
try {
await CohortModel.updateMany({ cohortGroupId: { $exists: true } }, { $unset: { cohortGroupId: "" } });
await CohortGroupModel.deleteMany({});
} catch (error) {
logger.error(error);
throw error;
}
},
};

0 comments on commit 3926d83

Please sign in to comment.