feat(#112): reprise après refus — dossier complet, email resoumission

- GET/PATCH reprise-dossier enrichis (parents, enfants, motivation, fiche AM)
- Front: lien mail, modale identify login, wizards préremplis, PATCH complet
- Email accusé resoumission aux parents avec n° de dossier
- Fixes préremplissage AM (dates, places, ValueKey étape 2)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-16 19:19:55 +02:00
co-authored by Cursor
parent c226c2fcdf
commit b99745e0fe
36 changed files with 2460 additions and 85 deletions
@@ -1,12 +1,29 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional, IsString, MaxLength, IsUUID } from 'class-validator';
import {
IsOptional,
IsString,
MaxLength,
IsUUID,
IsBoolean,
IsArray,
ValidateNested,
IsInt,
Min,
Max,
IsDateString,
Matches,
} from 'class-validator';
import { Type } from 'class-transformer';
import { EnfantRepriseDto } from './enfant-reprise.dto';
/** Body PUT /auth/reprise-resoumettre token + champs modifiables. Ticket #111 */
/** Body PATCH /auth/reprise-resoumettre token + champs modifiables du wizard. #111 + #112 */
export class ResoumettreRepriseDto {
@ApiProperty({ description: 'Token reprise (reçu par email)' })
@IsUUID()
token: string;
// --- Identité titulaire (parent principal ou AM) ---
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@@ -42,8 +59,147 @@ export class ResoumettreRepriseDto {
@MaxLength(10)
code_postal?: string;
@ApiProperty({ required: false, description: 'Pour AM' })
@ApiProperty({ required: false, description: 'Pour AM (URL photo existante)' })
@IsOptional()
@IsString()
photo_url?: string;
@ApiProperty({ required: false, description: 'Pour AM (nouvelle photo base64)' })
@IsOptional()
@IsString()
photo_base64?: string;
@ApiProperty({ required: false, description: 'Pour AM (nom fichier photo)' })
@IsOptional()
@IsString()
photo_filename?: string;
// --- Co-parent (reprise parent) ---
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_email?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_prenom?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_nom?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@Matches(/^(\+33|0)[1-9](\d{2}){4}$/, {
message: 'Le numéro de téléphone du co-parent doit être valide',
})
co_parent_telephone?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsBoolean()
co_parent_meme_adresse?: boolean;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_adresse?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_code_postal?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
co_parent_ville?: string;
// --- Motivation dossier famille ---
@ApiProperty({ required: false, description: 'Alias texte_motivation' })
@IsOptional()
@IsString()
@MaxLength(2000)
texte_motivation?: string;
@ApiProperty({ required: false, description: 'Alias presentation_dossier (inscription)' })
@IsOptional()
@IsString()
@MaxLength(2000)
presentation_dossier?: string;
@ApiProperty({ type: [EnfantRepriseDto], required: false })
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => EnfantRepriseDto)
enfants?: EnfantRepriseDto[];
// --- AM ---
@ApiProperty({ required: false })
@IsOptional()
@IsBoolean()
consentement_photo?: boolean;
@ApiProperty({ required: false })
@IsOptional()
@IsDateString()
date_naissance?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(100)
lieu_naissance_ville?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(100)
lieu_naissance_pays?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(50)
numero_agrement?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@Matches(/^[1-3]\d{4}(?:2A|2B|\d{2})\d{6}\d{2}$/, {
message: 'Le NIR doit contenir 15 caractères (chiffres, ou 2A/2B pour la Corse)',
})
nir?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsDateString()
date_agrement?: string;
@ApiProperty({ required: false })
@IsOptional()
@IsInt()
@Min(1)
@Max(10)
capacite_accueil?: number;
@ApiProperty({ required: false })
@IsOptional()
@IsInt()
@Min(0)
@Max(10)
places_disponibles?: number;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MaxLength(2000)
biographie?: string;
}