feat(#132): photo à la création enfant staff (multipart + bools).
Transform "true"/"false" pour class-validator ; multipart staff
avec FileInterceptor('photo') inchangé côté stockage.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
26e9738ec7
commit
f7ac628445
@ -1,4 +1,5 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
@ -12,6 +13,17 @@ import {
|
||||
} from 'class-validator';
|
||||
import { GenreType, StatutEnfantType } from 'src/entities/children.entity';
|
||||
|
||||
/** Multipart envoie des strings ("true"/"false") — JSON envoie déjà des booleans. */
|
||||
function toBoolean({ value }: { value: unknown }): boolean | unknown {
|
||||
if (typeof value === 'boolean') return value;
|
||||
if (typeof value === 'string') {
|
||||
const v = value.trim().toLowerCase();
|
||||
if (v === 'true' || v === '1') return true;
|
||||
if (v === 'false' || v === '0' || v === '') return false;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export class CreateEnfantsDto {
|
||||
@ApiProperty({ enum: StatutEnfantType, example: StatutEnfantType.SANS_GARDE })
|
||||
@IsEnum(StatutEnfantType)
|
||||
@ -53,6 +65,7 @@ export class CreateEnfantsDto {
|
||||
photo_url?: string;
|
||||
|
||||
@ApiProperty({ default: false })
|
||||
@Transform(toBoolean)
|
||||
@IsBoolean()
|
||||
consent_photo: boolean;
|
||||
|
||||
@ -62,6 +75,7 @@ export class CreateEnfantsDto {
|
||||
consent_photo_at?: string;
|
||||
|
||||
@ApiProperty({ default: false })
|
||||
@Transform(toBoolean)
|
||||
@IsBoolean()
|
||||
is_multiple: boolean;
|
||||
|
||||
|
||||
@ -58,8 +58,8 @@ const photoMulterOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Multer uniquement si Content-Type multipart (parcours parent).
|
||||
* Les appels staff JSON (#132) passent sans interceptor fichier.
|
||||
* Multer uniquement si Content-Type multipart (parent ou staff + photo).
|
||||
* JSON sans photo (#132) passe sans interceptor fichier.
|
||||
*/
|
||||
@Injectable()
|
||||
class OptionalEnfantPhotoInterceptor implements NestInterceptor {
|
||||
@ -96,10 +96,15 @@ export class EnfantsController {
|
||||
@ApiOperation({
|
||||
summary: 'Créer un enfant',
|
||||
description:
|
||||
'PARENT : multipart éventuel, rattache au compte connecté. Staff : JSON + parent_user_id (foyer). Ticket #132.',
|
||||
'PARENT : multipart éventuel, rattache au compte connecté. ' +
|
||||
'Staff : parent_user_id obligatoire ; JSON sans photo OK ; avec photo → multipart (champ fichier `photo`, max 5 Mo). Ticket #132.',
|
||||
})
|
||||
@ApiConsumes('application/json', 'multipart/form-data')
|
||||
@ApiBody({ type: CreateEnfantsDto })
|
||||
@ApiBody({
|
||||
description:
|
||||
'Champs métier (+ parent_user_id côté staff). Fichier optionnel `photo` en multipart.',
|
||||
type: CreateEnfantsDto,
|
||||
})
|
||||
@UseInterceptors(OptionalEnfantPhotoInterceptor)
|
||||
create(
|
||||
@Body() dto: CreateEnfantsDto,
|
||||
|
||||
@ -37,7 +37,8 @@ export class EnfantsService {
|
||||
/**
|
||||
* Création d'un enfant.
|
||||
* - PARENT : rattache au parent connecté (multipart photo optionnel).
|
||||
* - Staff : JSON + `parent_user_id` obligatoire (foyer existant). Ticket #132.
|
||||
* - Staff : `parent_user_id` obligatoire ; JSON sans photo OK ;
|
||||
* avec photo → multipart (même stockage `/uploads/photos/...`). Ticket #132.
|
||||
*/
|
||||
async create(
|
||||
dto: CreateEnfantsDto,
|
||||
@ -67,7 +68,7 @@ export class EnfantsService {
|
||||
});
|
||||
if (exist) throw new ConflictException('Cet enfant existe déjà');
|
||||
|
||||
// Gestion de la photo uploadée (parcours parent multipart)
|
||||
// Gestion de la photo uploadée (multipart parent ou staff)
|
||||
let photoUrl = dto.photo_url;
|
||||
let consentAt: Date | undefined;
|
||||
if (photoFile) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user