feat(materiel): connect catalogue and detail to api
This commit is contained in:
@@ -10,7 +10,9 @@ import { apiRouter } from './routes';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(cors({ origin: env.frontendUrl }));
|
||||
// En développement, on accepte toute origine locale (le front Flutter tourne sur
|
||||
// un port variable) ; en production, seule l'origine du frontend est autorisée.
|
||||
app.use(cors({ origin: env.nodeEnv === 'development' ? true : env.frontendUrl }));
|
||||
app.use(express.json());
|
||||
app.use(requestLogger);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { AppError } from '../errors/app-error';
|
||||
import { listerCatalogue } from '../services/materiel.service';
|
||||
import { toMaterielResponse } from '../dtos/materiel.dto';
|
||||
import { listerCatalogue, obtenirDetailMateriel } from '../services/materiel.service';
|
||||
import { toMaterielDetailResponse, toMaterielResponse } from '../dtos/materiel.dto';
|
||||
|
||||
function parseCategorieId(value: unknown): number | undefined {
|
||||
if (value === undefined) {
|
||||
@@ -16,6 +16,18 @@ function parseCategorieId(value: unknown): number | undefined {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function parseId(value: unknown): number {
|
||||
if (typeof value !== 'string') {
|
||||
throw new AppError(400, 'id invalide');
|
||||
}
|
||||
|
||||
const parsed = Number(value);
|
||||
if (!Number.isInteger(parsed) || parsed <= 0) {
|
||||
throw new AppError(400, 'id invalide');
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export async function getCatalogue(req: Request, res: Response): Promise<void> {
|
||||
const user = req.user;
|
||||
if (!user) {
|
||||
@@ -28,3 +40,13 @@ export async function getCatalogue(req: Request, res: Response): Promise<void> {
|
||||
const materiels = await listerCatalogue(user.campusId, { categorieId, recherche });
|
||||
res.json({ data: materiels.map(toMaterielResponse) });
|
||||
}
|
||||
|
||||
export async function getMaterielDetail(req: Request, res: Response): Promise<void> {
|
||||
const user = req.user;
|
||||
if (!user) {
|
||||
throw new AppError(401, 'Authentification requise');
|
||||
}
|
||||
|
||||
const materiel = await obtenirDetailMateriel(parseId(req.params.id), user.campusId);
|
||||
res.json({ data: toMaterielDetailResponse(materiel) });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MaterielAvecCategorie } from '../repositories/materiel.repository';
|
||||
import { MaterielAvecCategorie, MaterielDetail } from '../repositories/materiel.repository';
|
||||
|
||||
export interface CategorieResponse {
|
||||
id: number;
|
||||
@@ -16,6 +16,15 @@ export interface MaterielResponse {
|
||||
categorie: CategorieResponse;
|
||||
}
|
||||
|
||||
export interface MaterielDetailResponse extends MaterielResponse {
|
||||
campus: {
|
||||
id: number;
|
||||
nom: string;
|
||||
ville: string;
|
||||
};
|
||||
accessoires: string[];
|
||||
}
|
||||
|
||||
export function toMaterielResponse(materiel: MaterielAvecCategorie): MaterielResponse {
|
||||
return {
|
||||
id: materiel.id,
|
||||
@@ -31,3 +40,15 @@ export function toMaterielResponse(materiel: MaterielAvecCategorie): MaterielRes
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function toMaterielDetailResponse(materiel: MaterielDetail): MaterielDetailResponse {
|
||||
return {
|
||||
...toMaterielResponse(materiel),
|
||||
campus: {
|
||||
id: materiel.campus.id,
|
||||
nom: materiel.campus.nom,
|
||||
ville: materiel.campus.ville,
|
||||
},
|
||||
accessoires: materiel.accessoires.map((liaison) => liaison.accessoire.nom),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,13 @@ import { Materiel, Prisma } from '@prisma/client';
|
||||
import { prisma } from '../db/prisma';
|
||||
|
||||
export type MaterielAvecCategorie = Prisma.MaterielGetPayload<{ include: { categorie: true } }>;
|
||||
export type MaterielDetail = Prisma.MaterielGetPayload<{
|
||||
include: {
|
||||
categorie: true;
|
||||
campus: true;
|
||||
accessoires: { include: { accessoire: true } };
|
||||
};
|
||||
}>;
|
||||
|
||||
export interface CatalogueFiltres {
|
||||
categorieId?: number;
|
||||
@@ -39,3 +46,17 @@ export function findDisponiblesParCampus(
|
||||
export function findById(id: number): Promise<Materiel | null> {
|
||||
return prisma.materiel.findUnique({ where: { id } });
|
||||
}
|
||||
|
||||
export function findDetailParCampus(id: number, campusId: number): Promise<MaterielDetail | null> {
|
||||
return prisma.materiel.findFirst({
|
||||
where: { id, campusId, actif: true },
|
||||
include: {
|
||||
categorie: true,
|
||||
campus: true,
|
||||
accessoires: {
|
||||
include: { accessoire: true },
|
||||
orderBy: { accessoire: { nom: 'asc' } },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Router } from 'express';
|
||||
import { getCatalogue } from '../controllers/materiel.controller';
|
||||
import { getCatalogue, getMaterielDetail } from '../controllers/materiel.controller';
|
||||
|
||||
export const materielRoutes: Router = Router();
|
||||
|
||||
materielRoutes.get('/', getCatalogue);
|
||||
materielRoutes.get('/:id', getMaterielDetail);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import {
|
||||
findDisponiblesParCampus,
|
||||
findDetailParCampus,
|
||||
CatalogueFiltres,
|
||||
MaterielDetail,
|
||||
MaterielAvecCategorie,
|
||||
} from '../repositories/materiel.repository';
|
||||
import { AppError } from '../errors/app-error';
|
||||
|
||||
/* RG10 : après identification, seuls les matériels disponibles du campus de
|
||||
l'étudiant sont affichés. */
|
||||
@@ -12,3 +15,11 @@ export function listerCatalogue(
|
||||
): Promise<MaterielAvecCategorie[]> {
|
||||
return findDisponiblesParCampus(campusId, filtres);
|
||||
}
|
||||
|
||||
export async function obtenirDetailMateriel(id: number, campusId: number): Promise<MaterielDetail> {
|
||||
const materiel = await findDetailParCampus(id, campusId);
|
||||
if (!materiel) {
|
||||
throw new AppError(404, 'Materiel introuvable');
|
||||
}
|
||||
return materiel;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user