Skip to content

Commit

Permalink
feat: mise en place de la pagination et des filtres pour les apprenan…
Browse files Browse the repository at this point in the history
…ts (#3995)
  • Loading branch information
nkrmr authored Jan 20, 2025
1 parent f463d3f commit 795fa7b
Show file tree
Hide file tree
Showing 14 changed files with 261 additions and 296 deletions.
28 changes: 21 additions & 7 deletions server/src/common/actions/mission-locale/mission-locale.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ export const buildFiltersForMissionLocale = (effectifFilters: IEffectifsFiltersM
last_update_order = null,
} = effectifFilters;

const today = new Date();
const adultThreshold = new Date(today.setFullYear(today.getFullYear() - 18));

const filter = [
...filterByDernierStatutPipeline(
(statut as Array<StatutApprenant>) ?? [
Expand All @@ -81,9 +84,20 @@ export const buildFiltersForMissionLocale = (effectifFilters: IEffectifsFiltersM
}, []),
}
: {}),
...(rqth !== null ? { "apprenant.rqth": rqth } : {}),
...(mineur !== null
? { "apprenant.date_de_naissance": { $gte: new Date(new Date().setFullYear(new Date().getFullYear() - 18)) } }
...(rqth !== null && rqth.length > 0
? {
$or: [
{ "apprenant.rqth": { $in: rqth } },
...(rqth.includes(false) ? [{ "apprenant.rqth": { $exists: false } }] : []),
],
}
: {}),
...(mineur !== null && mineur.length > 0
? mineur.includes(true) && mineur.includes(false)
? {}
: mineur.includes(true)
? { "apprenant.date_de_naissance": { $gte: adultThreshold } }
: { "apprenant.date_de_naissance": { $lt: adultThreshold } }
: {}),
...(niveaux !== null ? { "formation.niveau": { $in: niveaux } } : {}),
...(code_insee !== null ? { "apprenant.adresse.code_insee": { $in: code_insee } } : {}),
Expand Down Expand Up @@ -170,7 +184,7 @@ export const getPaginatedEffectifsByMissionLocaleId = async (
$addToSet: {
code_insee: "$apprenant.adresse.code_insee",
code_postal: "$apprenant.adresse.code_postal",
nom: "$apprenant.adresse.commune",
commune: "$apprenant.adresse.commune",
},
},
},
Expand All @@ -186,7 +200,7 @@ export const getPaginatedEffectifsByMissionLocaleId = async (
_id: 0,
code_insee: "$commune.code_insee",
code_postal: "$commune.code_postal",
nom: "$commune.nom",
commune: "$commune.commune",
},
},
];
Expand Down Expand Up @@ -225,7 +239,7 @@ export const getPaginatedEffectifsByMissionLocaleId = async (
{
$facet: {
pagination: [{ $count: "total" }, { $addFields: { page, limit } }],
data: [{ $skip: (page - 1) * limit }, { $limit: limit }],
data: [{ $skip: page * limit }, { $limit: limit }],
},
},
{ $unwind: { path: "$pagination", preserveNullAndEmptyArrays: true } },
Expand All @@ -243,7 +257,7 @@ export const getPaginatedEffectifsByMissionLocaleId = async (
const resultAdresse = await effectifsDb().aggregate(adresseFilterAggregation).toArray();

if (!resultEffectif || resultEffectif?.data.length === 0) {
return { pagination: { total: 0, page, limit }, data: [], filter: [] };
return { pagination: { total: 0, page, limit }, data: [], filter: resultAdresse };
}

const { pagination, data } = resultEffectif;
Expand Down
7 changes: 4 additions & 3 deletions shared/constants/effectifs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export type StatutApprenant = (typeof STATUT_APPRENANT)[keyof typeof STATUT_APPR

export const STATUT_APPRENANT_VALUES = Object.values(STATUT_APPRENANT);

const STATUT_NAME: { [key in StatutApprenant]: string } = {
export const STATUT_NAME: { [key in StatutApprenant]: string } = {
ABANDON: "Abandon",
PRE_INSCRIT: "Pré-inscrit",
INSCRIT: "Sans contrat",
INSCRIT: "Inscrit sans contrat",
APPRENTI: "Apprenti",
RUPTURANT: "Rupturant",
RUPTURANT: "Rupture de contrat",
FIN_DE_FORMATION: "Fin de formation",
};

Expand All @@ -44,6 +44,7 @@ export enum MOTIF_SUPPRESSION {
Autre = "AUTRE",
Doublon = "DOUBLON",
}

export const MOTIF_SUPPRESSION_LABEL = [
{
id: MOTIF_SUPPRESSION.MauvaiseManip,
Expand Down
1 change: 1 addition & 0 deletions shared/models/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./formationsCatalogue.model";
export * from "./invitations.model";
export * from "./jwtSessions.model";
export * from "./maintenanceMessages.model";
export * from "./missionLocaleEffectif.model";
export * from "./organisations.model";
export * from "./organismes.model";
export * from "./organismesReferentiel.model";
Expand Down
8 changes: 8 additions & 0 deletions shared/models/data/missionLocaleEffectif.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export enum SITUATION_ENUM {
NON_CONTACTE = "NON_CONTACTE",
}

export enum SITUATION_LABEL_ENUM {
CONTACTE_AVEC_SUIVI = "Contacté, soutien nécessaire",
CONTACT_SANS_SUIVI = "Contacté, pas de suivi nécessaire",
DEJA_SUIVI = "Déjà accompagné par ML",
INJOIGNABLE = "Injoignable",
NON_CONTACTE = "Non contacté",
}

export enum STATUT_JEUNE_MISSION_LOCALE {
CONTRAT_SIGNE_NON_DEMARRE = "CONTRAT_SIGNE_NON_DEMARRE",
RETOUR_EN_VOIE_SCOLAIRE = "RETOUR_EN_VOIE_SCOLAIRE",
Expand Down
8 changes: 8 additions & 0 deletions shared/models/parts/adresseSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,11 @@ export const zAdresse = zodOpenApi.object({
export const zAdresseWithMissionLocale = zAdresse.extend({
mission_locale_id: zodOpenApi.number({ description: "Id de mission locale" }).optional(),
});

export const zCommune = zAdresse.pick({
code_insee: true,
code_postal: true,
commune: true,
});

export type Commune = zodOpenApi.infer<typeof zCommune>;
4 changes: 2 additions & 2 deletions shared/models/routes/mission-locale/missionLocale.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const effectifsFiltersMissionLocaleSchema = {
statut: z
.array(z.enum([STATUT_APPRENANT.ABANDON, STATUT_APPRENANT.RUPTURANT, STATUT_APPRENANT.INSCRIT]).optional())
.optional(),
rqth: zBooleanStringSchema.optional(),
mineur: zBooleanStringSchema.optional(),
rqth: z.array(zBooleanStringSchema).optional(),
mineur: z.array(zBooleanStringSchema).optional(),
niveaux: z.array(z.string()).optional(),
code_insee: z.array(z.string()).optional(),
search: z.string().optional(),
Expand Down
14 changes: 7 additions & 7 deletions ui/components/Filter/FilterList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SimpleOverlayMenu from "@/modules/dashboard/SimpleOverlayMenu";
interface FilterListProps {
filterKey: string;
displayName: string;
options: string[];
options: Record<string, string>;
selectedValues: string[];
onChange: (selectedValues: string[]) => void;
isOpen: boolean;
Expand All @@ -25,11 +25,11 @@ const FilterList: React.FC<FilterListProps> = ({
setIsOpen,
sortOrder = "asc",
}) => {
const sortedOptions = [...options].sort((a, b) => {
const sortedOptions = Object.entries(options).sort(([_keyA, valA], [_keyB, valB]) => {
if (sortOrder === "asc") {
return a.localeCompare(b);
return valA.localeCompare(valB);
}
return b.localeCompare(a);
return valB.localeCompare(valA);
});

return (
Expand All @@ -44,9 +44,9 @@ const FilterList: React.FC<FilterListProps> = ({
<SimpleOverlayMenu onClose={() => setIsOpen(false)} width="auto" p="3w">
<CheckboxGroup defaultValue={selectedValues} size="sm" onChange={onChange}>
<Stack>
{sortedOptions.map((option, index) => (
<Checkbox key={index} value={option} iconSize="0.5rem">
{capitalizeWords(option)}
{sortedOptions.map(([key, value]) => (
<Checkbox key={key} value={key} iconSize="0.5rem">
{capitalizeWords(value)}
</Checkbox>
))}
</Stack>
Expand Down
2 changes: 1 addition & 1 deletion ui/components/Page/SimplePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function SimplePage({ title, children }: Props) {
<Head>{title && <title>{title}</title>}</Head>
<Header />
<NavigationMenu />
<Box minH={"40vh"} flexGrow="1" pb={8}>
<Box minH={"40vh"} flexGrow="1" pb={20}>
{children}
</Box>
<Link
Expand Down
16 changes: 9 additions & 7 deletions ui/components/Page/components/NavigationMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -305,13 +305,15 @@ const MenuQuestions = () => {
>
Centre d’aide
</MenuItem>
<MenuItem
as="a"
href="/referencement-organisme"
onClick={() => trackPlausibleEvent("clic_homepage_referencement_organisme")}
>
Référencement de votre organisme
</MenuItem>
{organisationType !== ORGANISATION_TYPE.MISSION_LOCALE && (
<MenuItem
as="a"
href="/referencement-organisme"
onClick={() => trackPlausibleEvent("clic_homepage_referencement_organisme")}
>
Référencement de votre organisme
</MenuItem>
)}
<MenuItem as="a" href="/glossaire">
Glossaire
</MenuItem>
Expand Down
8 changes: 6 additions & 2 deletions ui/modules/dashboard/DashboardMissionLocale.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ArrowForwardIcon } from "@chakra-ui/icons";
import { Box, Center, Container, Grid, GridItem, Heading, HStack, Spinner, Text } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
import { getOrganisationLabel } from "shared";

import { _get, _post } from "@/common/httpClient";
Expand Down Expand Up @@ -62,7 +63,7 @@ const DashboardMissionLocale = () => {
<HStack alignItems={"center"} my={8}>
<InfoSquare color="bluefrance" bg="white" boxSize="5" />
<Text>
Les apprenants et CFA restitués sur votre espace sont domiciliés sur votre zone de couverture géographique.
Les apprenants restitués sur votre espace sont domiciliés sur votre zone de couverture géographique.
</Text>
</HStack>
<Text fontSize={14}>
Expand All @@ -79,6 +80,9 @@ const DashboardMissionLocale = () => {
</Link>{" "}
(Dépôt des Contrats d’Alternance) peuvent apparaître pour ces derniers.
</Text>
<Text mt={4}>
Le <strong>{format(new Date(), "dd MMMM yyyy")}</strong>
</Text>
</Container>
<Container maxW="xl" px={8}>
<Grid templateColumns="repeat(3, 1fr)" gap={4} maxW="xl" mx="auto" minH={200}>
Expand Down Expand Up @@ -133,7 +137,7 @@ const DashboardMissionLocale = () => {
</GridItem>
</Grid>
<Link href="/apprenants" color="action-high-blue-france" borderBottom="1px" mt={6}>
Voir les listes nominatives des apprenants
Voir la liste nominative des apprenants
<ArrowForwardIcon />
</Link>
</Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Box,
Button,
HStack,
Text,
Table,
Expand Down Expand Up @@ -39,6 +40,7 @@ const getLastStatut = (statut: any) => {
const ApprenantsDetails = ({ row }) => {
const [statutCorrect, setStatutCorrect] = useState("");
const [franceTravail, setFranceTravail] = useState("");
const [apprenantStatut, setApprenantStatut] = useState<boolean>(false);

const apprenant = row.original.apprenant;
const statut = row.original.statut;
Expand All @@ -49,8 +51,8 @@ const ApprenantsDetails = ({ row }) => {

return (
<Box borderWidth="2px" borderStyle="solid" borderColor="#E3E3FD" p={4} mt={3}>
<Flex bg="#F9F8F6">
<Box p={4} flex="1">
<Flex bg="white" gap={2}>
<Box p={6} flex="1" bg="#F9F8F6">
<Heading as="h3" color="gray.900" fontSize="gamma" fontWeight="700" mb={3}>
Ses informations
</Heading>
Expand Down Expand Up @@ -111,7 +113,7 @@ const ApprenantsDetails = ({ row }) => {
</Tbody>
</Table>
</Box>
<Box p={4} flex="1" maxW="600px" mx="auto">
<Box p={6} flex="1" maxW="600px" mx="auto" bg="#F9F8F6">
<Heading as="h3" color="gray.900" fontSize="gamma" fontWeight="700" mb={4}>
Vos commentaires et retours
</Heading>
Expand All @@ -124,23 +126,34 @@ const ApprenantsDetails = ({ row }) => {
<Text fontSize="md" mb={3}>
1. Le statut du jeune affiché est-il correct ?
</Text>
<RadioGroup onChange={setStatutCorrect} value={statutCorrect}>
<RadioGroup
onChange={(value) => {
setStatutCorrect(value);
setApprenantStatut(value === "non");
}}
value={statutCorrect}
>
<HStack spacing={4}>
<Radio value="oui">Oui</Radio>
<Radio value="non">Non</Radio>
</HStack>
</RadioGroup>

<Text fontSize="sm" mt={4} mb={2}>
Veuillez préciser son statut actuel.
</Text>
<Select placeholder="Sélectionner une option">
<option value="contacte_soutien">Contacté, soutien nécessaire</option>
<option value="contacte_pas_de_suivi">Contacté, pas de suivi nécessaire</option>
<option value="deja_accompagne">Déjà accompagné par ML</option>
<option value="injoignable">Injoignable</option>
<option value="non_contacte">Non contacté</option>
</Select>
{apprenantStatut && (
<>
<Text fontSize="sm" mt={4} mb={2}>
Veuillez préciser son statut actuel.
</Text>
<Select placeholder="Sélectionner une option">
<option value="contacte_soutien">Contrat signé non démarré</option>
<option value="contacte_pas_de_suivi">Retour en voie scolaire initiale</option>
<option value="deja_accompagne">Abandon de la formation</option>
<option value="injoignable">Rupture de contrat d’apprentissage</option>
<option value="non_contacte">Décrochage (abandon)</option>
<option value="non_contacte">Autre raison</option>
</Select>
</>
)}
</Box>

<Box mb={6}>
Expand All @@ -161,11 +174,16 @@ const ApprenantsDetails = ({ row }) => {
3. Notez ici des informations recueillies auprès du jeune et sa situation, que vous jugez utiles de
partager.{" "}
<Text as="span" fontSize="sm" color="gray.500">
(150 caractères maximum)
(200 caractères maximum)
</Text>
</Text>
<Textarea placeholder="Vos retours sur l’accompagnement du jeune" maxLength={150} />
<Textarea placeholder="Vos retours sur l’accompagnement du jeune" maxLength={200} />
</Box>
<Flex justifyContent="flex-end">
<Button variant="secondary" size={"sm"}>
Enregistrer
</Button>
</Flex>
</Box>
</Flex>

Expand Down
Loading

0 comments on commit 795fa7b

Please sign in to comment.