feat(responsable): improve loan supervision table

This commit is contained in:
SaidSoighiri94
2026-07-24 15:00:16 +02:00
parent 4ee593c8d4
commit 5121cac657
6 changed files with 748 additions and 29 deletions
@@ -13,15 +13,18 @@ import {
marquerNotificationResponsableLue,
marquerToutesNotificationsResponsableLues,
parseAnomalieId,
parseAnneeEmprunt,
parseEmpruntId,
parseCategorieId,
parseLu,
parseNotificationId,
parseOrdreTri,
parseDateQuery,
parsePositiveIntQuery,
parseStatutAnomalie,
parseStatutMateriel,
parseStatutEmprunt,
parseTriEmprunt,
} from '../services/responsable.service';
import {
toAnomalieResponsableResponse,
@@ -77,7 +80,17 @@ export async function getEmprunts(req: Request, res: Response): Promise<void> {
}
const statut = parseStatutEmprunt(req.query.statut);
const emprunts = await listerEmpruntsResponsable(user.roleCode, user.campusId, { statut });
const annee = parseAnneeEmprunt(req.query.annee);
const recherche = typeof req.query.q === 'string' ? req.query.q.trim() : undefined;
const tri = parseTriEmprunt(req.query.tri);
const ordre = parseOrdreTri(req.query.ordre);
const emprunts = await listerEmpruntsResponsable(user.roleCode, user.campusId, {
statut,
annee,
recherche: recherche || undefined,
tri,
ordre,
});
res.json({ data: emprunts.map(toEmpruntResponsableResponse) });
}
@@ -73,6 +73,16 @@ export type HistoriqueResponsable = Prisma.HistoriqueGetPayload<{
export interface EmpruntResponsableFiltres {
statut?: string;
annee?: number;
recherche?: string;
tri?:
| 'dateEmprunt'
| 'dateRetourPrevue'
| 'dateRetourReelle'
| 'materiel'
| 'etudiant'
| 'statut';
ordre?: 'asc' | 'desc';
}
export interface MaterielResponsableFiltres {
@@ -184,16 +194,55 @@ export function findEmpruntsResponsable(
campusId: number,
filtres: EmpruntResponsableFiltres,
): Promise<EmpruntResponsable[]> {
const ordre = filtres.ordre ?? 'desc';
const periode = filtres.annee
? {
gte: new Date(Date.UTC(filtres.annee, 0, 1)),
lt: new Date(Date.UTC(filtres.annee + 1, 0, 1)),
}
: undefined;
const orderBy: Prisma.EmpruntOrderByWithRelationInput[] = (() => {
switch (filtres.tri) {
case 'dateRetourPrevue':
return [{ dateRetourPrevue: ordre }, { id: 'desc' }];
case 'dateRetourReelle':
return [{ dateRetourReelle: ordre }, { id: 'desc' }];
case 'materiel':
return [{ materiel: { nom: ordre } }, { id: 'desc' }];
case 'etudiant':
return [{ utilisateur: { nom: ordre } }, { id: 'desc' }];
case 'statut':
return [{ statut: ordre }, { id: 'desc' }];
default:
return [{ dateEmprunt: ordre }, { id: 'desc' }];
}
})();
return prisma.emprunt.findMany({
where: {
campusId,
...(filtres.statut ? { statut: filtres.statut } : {}),
...(periode ? { dateEmprunt: periode } : {}),
...(filtres.recherche
? {
OR: [
{ materiel: { nom: { contains: filtres.recherche } } },
{ materiel: { reference: { contains: filtres.recherche } } },
{ materiel: { marque: { contains: filtres.recherche } } },
{ materiel: { modele: { contains: filtres.recherche } } },
{ utilisateur: { nom: { contains: filtres.recherche } } },
{ utilisateur: { prenom: { contains: filtres.recherche } } },
{ utilisateur: { email: { contains: filtres.recherche } } },
{ utilisateur: { classe: { contains: filtres.recherche } } },
],
}
: {}),
},
include: {
utilisateur: true,
materiel: { include: { categorie: true } },
},
orderBy: { dateEmprunt: 'desc' },
orderBy,
});
}
@@ -45,8 +45,22 @@ export interface DashboardResponsableData {
export interface ListerEmpruntsResponsableFiltres {
statut?: StatutEmprunt;
annee?: number;
recherche?: string;
tri?: TriEmpruntResponsable;
ordre?: OrdreTri;
}
export type TriEmpruntResponsable =
| 'dateEmprunt'
| 'dateRetourPrevue'
| 'dateRetourReelle'
| 'materiel'
| 'etudiant'
| 'statut';
export type OrdreTri = 'asc' | 'desc';
export interface ListerMaterielsResponsableFiltres {
statut?: StatutMateriel;
categorieId?: number;
@@ -139,6 +153,49 @@ export function parseStatutEmprunt(value: unknown): StatutEmprunt | undefined {
return value as StatutEmprunt;
}
export function parseAnneeEmprunt(value: unknown): number | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value !== 'string') {
throw new AppError(400, 'annee invalide');
}
const annee = Number(value);
if (!Number.isInteger(annee) || annee < 2000 || annee > 2100) {
throw new AppError(400, 'annee invalide');
}
return annee;
}
export function parseTriEmprunt(value: unknown): TriEmpruntResponsable | undefined {
if (value === undefined) {
return undefined;
}
const valeurs: readonly TriEmpruntResponsable[] = [
'dateEmprunt',
'dateRetourPrevue',
'dateRetourReelle',
'materiel',
'etudiant',
'statut',
];
if (typeof value !== 'string' || !valeurs.includes(value as TriEmpruntResponsable)) {
throw new AppError(400, 'tri invalide');
}
return value as TriEmpruntResponsable;
}
export function parseOrdreTri(value: unknown): OrdreTri | undefined {
if (value === undefined) {
return undefined;
}
if (value !== 'asc' && value !== 'desc') {
throw new AppError(400, 'ordre invalide');
}
return value;
}
export function parseStatutMateriel(value: unknown): StatutMateriel | undefined {
if (value === undefined) {
return undefined;