From 671da7175262d57a5df2289ff9b9c982ee54ca0f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Tue, 16 Jun 2026 16:28:41 +0200 Subject: [PATCH] =?UTF-8?q?feat(#112):=20reprise=20apr=C3=A8s=20refus=20vi?= =?UTF-8?q?a=20lien=20e-mail=20(/reprise=3Ftoken=3D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Branche le flux front : chargement du dossier, session reprise, wizards parent/AM préremplis et resoumission via reprise-resoumettre. Co-authored-by: Cursor --- frontend/lib/config/app_router.dart | 6 + frontend/lib/models/am_registration_data.dart | 45 +++++ frontend/lib/models/reprise_dossier.dart | 53 ++++++ .../lib/models/user_registration_data.dart | 37 +++++ .../auth/am_register_step1_screen.dart | 3 +- .../auth/am_register_step4_screen.dart | 61 ++++++- .../auth/parent_register_step1_screen.dart | 3 +- .../auth/parent_register_step5_screen.dart | 60 +++++++ .../screens/auth/reprise_entry_screen.dart | 154 ++++++++++++++++++ frontend/lib/services/api/api_config.dart | 3 + frontend/lib/services/auth_service.dart | 86 ++++++++++ frontend/lib/services/reprise_session.dart | 67 ++++++++ 12 files changed, 575 insertions(+), 3 deletions(-) create mode 100644 frontend/lib/models/reprise_dossier.dart create mode 100644 frontend/lib/screens/auth/reprise_entry_screen.dart create mode 100644 frontend/lib/services/reprise_session.dart diff --git a/frontend/lib/config/app_router.dart b/frontend/lib/config/app_router.dart index 4dfe66e..693c424 100644 --- a/frontend/lib/config/app_router.dart +++ b/frontend/lib/config/app_router.dart @@ -21,6 +21,7 @@ import '../screens/auth/am_register_step4_screen.dart'; import '../screens/auth/create_password_screen.dart'; import '../screens/auth/forgot_password_screen.dart'; import '../screens/auth/reset_password_screen.dart'; +import '../screens/auth/reprise_entry_screen.dart'; import '../screens/home/home_screen.dart'; import '../screens/administrateurs/admin_dashboardScreen.dart'; import '../screens/gestionnaire/gestionnaire_dashboard_screen.dart'; @@ -66,6 +67,11 @@ class AppRouter { builder: (BuildContext context, GoRouterState state) => ResetPasswordScreen(token: state.uri.queryParameters['token']), ), + GoRoute( + path: '/reprise', + builder: (BuildContext context, GoRouterState state) => + RepriseEntryScreen(token: state.uri.queryParameters['token']), + ), GoRoute( path: '/home', builder: (BuildContext context, GoRouterState state) => const HomeScreen(), diff --git a/frontend/lib/models/am_registration_data.dart b/frontend/lib/models/am_registration_data.dart index 44604df..d61cbdd 100644 --- a/frontend/lib/models/am_registration_data.dart +++ b/frontend/lib/models/am_registration_data.dart @@ -33,6 +33,9 @@ class AmRegistrationData extends ChangeNotifier { /// Places libres actuellement (0 ≤ valeur ≤ capacité) — API `places_disponibles`. int? placesAvailable; + /// Photo déjà en base lors d'une reprise (#112) — sert pour l'affichage et `photo_url` API. + String? repriseExistingPhotoUrl; + // Step 3: Presentation & CGU String presentationText = ''; bool cguAccepted = false; @@ -104,6 +107,43 @@ class AmRegistrationData extends ChangeNotifier { notifyListeners(); } + /// Reprise après refus (#112). + void resetForReprise({ + required String firstName, + required String lastName, + required String phone, + required String email, + required String streetAddress, + required String postalCode, + required String city, + String? existingPhotoUrl, + }) { + this.firstName = firstName; + this.lastName = lastName; + this.phone = phone; + this.email = email; + this.streetAddress = streetAddress; + this.postalCode = postalCode; + this.city = city; + password = ''; + repriseExistingPhotoUrl = existingPhotoUrl; + photoPath = existingPhotoUrl; + photoBytes = null; + photoFilename = null; + photoConsent = false; + dateOfBirth = null; + birthCity = ''; + birthCountry = ''; + nir = ''; + agrementNumber = ''; + agreementDate = null; + capacity = null; + placesAvailable = null; + presentationText = ''; + cguAccepted = false; + notifyListeners(); + } + // --- Getters for validation or display --- bool get isStep1Complete => firstName.trim().length >= 2 && @@ -118,6 +158,8 @@ class AmRegistrationData extends ChangeNotifier { /// Photo réelle (pas seulement un placeholder asset). bool get _hasUserPhoto => (photoBytes != null && photoBytes!.isNotEmpty) || + (repriseExistingPhotoUrl != null && + repriseExistingPhotoUrl!.trim().isNotEmpty) || (photoPath != null && photoPath!.isNotEmpty && !photoPath!.startsWith('assets/')); @@ -145,6 +187,9 @@ class AmRegistrationData extends ChangeNotifier { bool get isRegistrationComplete => isStep1Complete && isStep2Complete && isStep3Complete; + /// Reprise (#112) : champs renvoyés par PATCH reprise-resoumettre. + bool get isRepriseSubmitReady => isStep1Complete && cguAccepted; + @override String toString() { return 'AmRegistrationData(' diff --git a/frontend/lib/models/reprise_dossier.dart b/frontend/lib/models/reprise_dossier.dart new file mode 100644 index 0000000..e0cc685 --- /dev/null +++ b/frontend/lib/models/reprise_dossier.dart @@ -0,0 +1,53 @@ +/// Réponse GET /auth/reprise-dossier. Ticket #111, #112. +class RepriseDossier { + final String id; + final String email; + final String? prenom; + final String? nom; + final String? telephone; + final String? adresse; + final String? ville; + final String? codePostal; + final String? numeroDossier; + final String role; + final String? photoUrl; + final String? genre; + final String? situationFamiliale; + + const RepriseDossier({ + required this.id, + required this.email, + this.prenom, + this.nom, + this.telephone, + this.adresse, + this.ville, + this.codePostal, + this.numeroDossier, + required this.role, + this.photoUrl, + this.genre, + this.situationFamiliale, + }); + + bool get isParent => role == 'parent'; + bool get isAm => role == 'assistante_maternelle'; + + factory RepriseDossier.fromJson(Map json) { + return RepriseDossier( + id: json['id']?.toString() ?? '', + email: json['email']?.toString() ?? '', + prenom: json['prenom']?.toString(), + nom: json['nom']?.toString(), + telephone: json['telephone']?.toString(), + adresse: json['adresse']?.toString(), + ville: json['ville']?.toString(), + codePostal: json['code_postal']?.toString(), + numeroDossier: json['numero_dossier']?.toString(), + role: json['role']?.toString() ?? '', + photoUrl: json['photo_url']?.toString(), + genre: json['genre']?.toString(), + situationFamiliale: json['situation_familiale']?.toString(), + ); + } +} diff --git a/frontend/lib/models/user_registration_data.dart b/frontend/lib/models/user_registration_data.dart index 5e8892f..5f29ce5 100644 --- a/frontend/lib/models/user_registration_data.dart +++ b/frontend/lib/models/user_registration_data.dart @@ -178,6 +178,43 @@ class UserRegistrationData extends ChangeNotifier { notifyListeners(); } + /// Reprise après refus (#112) : réinitialise le flux parent avec les données dossier. + void resetForReprise({ + required String firstName, + required String lastName, + required String phone, + required String email, + required String address, + required String postalCode, + required String city, + }) { + parent1 = ParentData( + firstName: firstName, + lastName: lastName, + phone: phone, + email: email, + address: address, + postalCode: postalCode, + city: city, + password: '', + ); + parent2 = null; + children.clear(); + motivationText = ''; + cguAccepted = false; + bankDetails = null; + attestationCafNumber = ''; + consentQuotientFamilial = false; + notifyListeners(); + } + + /// Reprise (#112) : coordonnées parent principal + CGU. + bool get isRepriseSubmitReady => + parent1.firstName.isNotEmpty && + parent1.lastName.isNotEmpty && + parent1.email.isNotEmpty && + cguAccepted; + // Méthode pour vérifier si toutes les données requises sont là (simplifié) bool isRegistrationComplete() { // Ajouter ici les validations nécessaires diff --git a/frontend/lib/screens/auth/am_register_step1_screen.dart b/frontend/lib/screens/auth/am_register_step1_screen.dart index 66129e6..1c5ca0f 100644 --- a/frontend/lib/screens/auth/am_register_step1_screen.dart +++ b/frontend/lib/screens/auth/am_register_step1_screen.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import '../../models/am_registration_data.dart'; import '../../widgets/personal_info_form_screen.dart'; +import '../../services/reprise_session.dart'; import '../../models/card_assets.dart'; class AmRegisterStep1Screen extends StatelessWidget { @@ -29,7 +30,7 @@ class AmRegisterStep1Screen extends StatelessWidget { cardColor: CardColorHorizontal.blue, initialData: initialData, minPersonNameLength: 2, - previousRoute: '/register-choice', + previousRoute: RepriseSession.isActive ? '/login' : '/register-choice', onSubmit: (data, {hasSecondPerson, sameAddress}) { registrationData.updateIdentityInfo( firstName: data.firstName, diff --git a/frontend/lib/screens/auth/am_register_step4_screen.dart b/frontend/lib/screens/auth/am_register_step4_screen.dart index 90f536f..3a93079 100644 --- a/frontend/lib/screens/auth/am_register_step4_screen.dart +++ b/frontend/lib/screens/auth/am_register_step4_screen.dart @@ -8,6 +8,7 @@ import '../../models/am_registration_data.dart'; import '../../models/card_assets.dart'; import '../../config/display_config.dart'; import '../../services/auth_service.dart'; +import '../../services/reprise_session.dart'; import '../../widgets/hover_relief_widget.dart'; import '../../widgets/image_button.dart'; import '../../widgets/custom_navigation_button.dart'; @@ -27,7 +28,21 @@ class _AmRegisterStep4ScreenState extends State { Future _submitAMRegistration(AmRegistrationData registrationData) async { if (_isSubmitting) return; - if (!registrationData.isRegistrationComplete) { + if (RepriseSession.isActive) { + if (!registrationData.isRepriseSubmitReady) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Vérifiez vos coordonnées et acceptez les conditions.', + style: GoogleFonts.merienda(fontSize: 14), + ), + backgroundColor: Colors.red.shade700, + ), + ); + return; + } + } else if (!registrationData.isRegistrationComplete) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -42,6 +57,22 @@ class _AmRegisterStep4ScreenState extends State { } setState(() => _isSubmitting = true); try { + if (RepriseSession.isActive) { + await AuthService.resoumettreReprise( + token: RepriseSession.token!, + prenom: registrationData.firstName, + nom: registrationData.lastName, + telephone: registrationData.phone, + adresse: registrationData.streetAddress, + ville: registrationData.city, + codePostal: registrationData.postalCode, + photoUrl: RepriseSession.photoUrl, + ); + RepriseSession.clear(); + if (!mounted) return; + _showRepriseConfirmationModal(context); + return; + } await AuthService.registerAM(registrationData); if (!mounted) return; _showConfirmationModal(context); @@ -244,6 +275,34 @@ class _AmRegisterStep4ScreenState extends State { ); } + void _showRepriseConfirmationModal(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: Text( + 'Dossier resoumis', + style: GoogleFonts.merienda(fontWeight: FontWeight.bold), + ), + content: Text( + 'Vos modifications ont été enregistrées. Votre dossier est de nouveau en attente de validation.', + style: GoogleFonts.merienda(fontSize: 14), + ), + actions: [ + TextButton( + child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)), + onPressed: () { + Navigator.of(dialogContext).pop(); + context.go('/login'); + }, + ), + ], + ); + }, + ); + } + void _showConfirmationModal(BuildContext context) { showDialog( context: context, diff --git a/frontend/lib/screens/auth/parent_register_step1_screen.dart b/frontend/lib/screens/auth/parent_register_step1_screen.dart index becad34..fd5de45 100644 --- a/frontend/lib/screens/auth/parent_register_step1_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step1_screen.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import '../../models/user_registration_data.dart'; import '../../widgets/personal_info_form_screen.dart'; +import '../../services/reprise_session.dart'; import '../../models/card_assets.dart'; class ParentRegisterStep1Screen extends StatelessWidget { @@ -29,7 +30,7 @@ class ParentRegisterStep1Screen extends StatelessWidget { title: 'Informations du Parent Principal', cardColor: CardColorHorizontal.peach, initialData: initialData, - previousRoute: '/register-choice', + previousRoute: RepriseSession.isActive ? '/login' : '/register-choice', onSubmit: (data, {hasSecondPerson, sameAddress}) { registrationData.updateParent1(ParentData( firstName: data.firstName, diff --git a/frontend/lib/screens/auth/parent_register_step5_screen.dart b/frontend/lib/screens/auth/parent_register_step5_screen.dart index 883f9ee..ac0512f 100644 --- a/frontend/lib/screens/auth/parent_register_step5_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step5_screen.dart @@ -14,6 +14,7 @@ import '../../widgets/personal_info_form_screen.dart'; import '../../widgets/child_card_widget.dart'; import '../../widgets/presentation_form_screen.dart'; import '../../services/auth_service.dart'; +import '../../services/reprise_session.dart'; class ParentRegisterStep5Screen extends StatefulWidget { const ParentRegisterStep5Screen({super.key}); @@ -27,8 +28,39 @@ class _ParentRegisterStep5ScreenState extends State { Future _submitRegistration(BuildContext context, UserRegistrationData data) async { if (_isSubmitting) return; + if (RepriseSession.isActive) { + if (!data.isRepriseSubmitReady) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Vérifiez vos coordonnées et acceptez les conditions.', + style: GoogleFonts.merienda(fontSize: 14), + ), + backgroundColor: Colors.red.shade700, + ), + ); + return; + } + } setState(() => _isSubmitting = true); try { + if (RepriseSession.isActive) { + final token = RepriseSession.token!; + final p = data.parent1; + await AuthService.resoumettreReprise( + token: token, + prenom: p.firstName, + nom: p.lastName, + telephone: p.phone, + adresse: p.address, + ville: p.city, + codePostal: p.postalCode, + ); + RepriseSession.clear(); + if (!context.mounted) return; + _showRepriseSuccessModal(context); + return; + } await AuthService.registerParent(data); if (!context.mounted) return; _showSuccessModal(context); @@ -279,6 +311,34 @@ class _ParentRegisterStep5ScreenState extends State { ); } + void _showRepriseSuccessModal(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: Text( + 'Dossier resoumis', + style: GoogleFonts.merienda(fontWeight: FontWeight.bold), + ), + content: Text( + 'Vos modifications ont été enregistrées. Votre dossier est de nouveau en attente de validation.', + style: GoogleFonts.merienda(fontSize: 14), + ), + actions: [ + TextButton( + child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)), + onPressed: () { + Navigator.of(dialogContext).pop(); + context.go('/login'); + }, + ), + ], + ); + }, + ); + } + void _showSuccessModal(BuildContext context) { showDialog( context: context, diff --git a/frontend/lib/screens/auth/reprise_entry_screen.dart b/frontend/lib/screens/auth/reprise_entry_screen.dart new file mode 100644 index 0000000..c2fff65 --- /dev/null +++ b/frontend/lib/screens/auth/reprise_entry_screen.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../../config/app_router.dart'; +import '../../services/auth_service.dart'; +import '../../services/reprise_session.dart'; + +/// Point d'entrée `/reprise?token=` — charge le dossier et ouvre le wizard (#112). +class RepriseEntryScreen extends StatefulWidget { + final String? token; + + const RepriseEntryScreen({super.key, this.token}); + + @override + State createState() => _RepriseEntryScreenState(); +} + +class _RepriseEntryScreenState extends State { + bool _loading = true; + String? _error; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) => _load()); + } + + Future _load() async { + final token = widget.token?.trim() ?? ''; + if (token.isEmpty) { + setState(() { + _loading = false; + _error = 'Lien invalide ou expiré.'; + }); + return; + } + + setState(() { + _loading = true; + _error = null; + }); + + try { + final dossier = await AuthService.getRepriseDossier(token); + if (!mounted) return; + + RepriseSession.clear(); + RepriseSession.start(token: token, dossier: dossier); + + if (dossier.isParent) { + RepriseSession.applyToParent(userRegistrationDataNotifier, dossier); + context.go('/parent-register-step1'); + return; + } + if (dossier.isAm) { + RepriseSession.applyToAm(amRegistrationDataNotifier, dossier); + context.go('/am-register-step1'); + return; + } + + RepriseSession.clear(); + setState(() { + _loading = false; + _error = 'Type de dossier non pris en charge pour la reprise.'; + }); + } catch (e) { + if (!mounted) return; + RepriseSession.clear(); + setState(() { + _loading = false; + _error = e is Exception + ? e.toString().replaceFirst('Exception: ', '') + : 'Impossible de charger votre dossier.'; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + Positioned.fill( + child: Image.asset( + 'assets/images/paper2.png', + fit: BoxFit.cover, + repeat: ImageRepeat.repeat, + ), + ), + Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 420), + child: Padding( + padding: const EdgeInsets.all(24), + child: _buildBody(), + ), + ), + ), + ], + ), + ); + } + + Widget _buildBody() { + if (_loading) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CircularProgressIndicator(), + const SizedBox(height: 20), + Text( + 'Chargement de votre dossier…', + style: GoogleFonts.merienda(fontSize: 16), + textAlign: TextAlign.center, + ), + ], + ); + } + + final err = _error ?? 'Lien invalide ou expiré.'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.error_outline, size: 48, color: Colors.red.shade700), + const SizedBox(height: 16), + Text( + 'Reprise du dossier', + style: GoogleFonts.merienda( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 12), + Text( + err, + style: GoogleFonts.merienda(fontSize: 14, color: Colors.black87), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + TextButton( + onPressed: () => context.go('/login'), + child: Text( + 'Retour à la connexion', + style: GoogleFonts.merienda( + decoration: TextDecoration.underline, + color: const Color(0xFF2D6A4F), + ), + ), + ), + ], + ); + } +} diff --git a/frontend/lib/services/api/api_config.dart b/frontend/lib/services/api/api_config.dart index 11d6a28..84f04a7 100644 --- a/frontend/lib/services/api/api_config.dart +++ b/frontend/lib/services/api/api_config.dart @@ -49,6 +49,9 @@ class ApiConfig { /// Ticket #127 — mot de passe oublié (back à livrer en parallèle). static const String forgotPassword = '/auth/forgot-password'; static const String resetPassword = '/auth/reset-password'; + /// Ticket #112 — reprise après refus (#111 back). + static const String repriseDossier = '/auth/reprise-dossier'; + static const String repriseResoumettre = '/auth/reprise-resoumettre'; // Users endpoints static const String users = '/users'; diff --git a/frontend/lib/services/auth_service.dart b/frontend/lib/services/auth_service.dart index 94f5d28..e89ca5e 100644 --- a/frontend/lib/services/auth_service.dart +++ b/frontend/lib/services/auth_service.dart @@ -8,6 +8,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../models/user.dart'; import '../models/am_registration_data.dart'; import '../models/user_registration_data.dart'; +import '../models/reprise_dossier.dart'; import '../utils/parent_registration_payload.dart'; import 'api/api_config.dart'; import 'api/tokenService.dart'; @@ -258,6 +259,91 @@ class AuthService { throw Exception(msg); } + /// Charge le dossier pour reprise (lien e-mail). GET /auth/reprise-dossier. Ticket #112. + static Future getRepriseDossier(String token) async { + final cleaned = token.trim(); + if (cleaned.isEmpty) { + throw Exception('Lien invalide ou expiré.'); + } + final uri = Uri.parse( + '${ApiConfig.baseUrl}${ApiConfig.repriseDossier}', + ).replace(queryParameters: {'token': cleaned}); + late final http.Response response; + try { + response = await http.get(uri, headers: ApiConfig.headers); + } on http.ClientException { + throw Exception( + 'Connexion à ${ApiConfig.baseUrl} impossible. Vérifiez votre réseau puis réessayez.', + ); + } + if (response.statusCode == 404) { + throw Exception('Lien invalide ou expiré.'); + } + if (response.statusCode != 200) { + final decoded = _tryDecodeJsonMap(response.body); + throw Exception(_extractErrorMessage(decoded, response.statusCode)); + } + final decoded = jsonDecode(response.body); + if (decoded is! Map) { + throw Exception('Réponse invalide du serveur.'); + } + return RepriseDossier.fromJson(decoded); + } + + /// Resoumission après refus. PATCH /auth/reprise-resoumettre. Ticket #112. + static Future resoumettreReprise({ + required String token, + String? prenom, + String? nom, + String? telephone, + String? adresse, + String? ville, + String? codePostal, + String? photoUrl, + }) async { + final cleaned = token.trim(); + if (cleaned.isEmpty) { + throw Exception('Lien invalide ou expiré.'); + } + final body = {'token': cleaned}; + if (prenom != null && prenom.trim().isNotEmpty) body['prenom'] = prenom.trim(); + if (nom != null && nom.trim().isNotEmpty) body['nom'] = nom.trim(); + if (telephone != null && telephone.trim().isNotEmpty) { + body['telephone'] = telephone.trim(); + } + if (adresse != null && adresse.trim().isNotEmpty) { + body['adresse'] = adresse.trim(); + } + if (ville != null && ville.trim().isNotEmpty) body['ville'] = ville.trim(); + if (codePostal != null && codePostal.trim().isNotEmpty) { + body['code_postal'] = codePostal.trim(); + } + if (photoUrl != null && photoUrl.trim().isNotEmpty) { + body['photo_url'] = photoUrl.trim(); + } + + late final http.Response response; + try { + response = await http.patch( + Uri.parse('${ApiConfig.baseUrl}${ApiConfig.repriseResoumettre}'), + headers: ApiConfig.headers, + body: jsonEncode(body), + ); + } on http.ClientException { + throw Exception( + 'Connexion à ${ApiConfig.baseUrl} impossible. Vérifiez votre réseau puis réessayez.', + ); + } + if (response.statusCode == 404) { + throw Exception('Lien invalide ou expiré.'); + } + if (response.statusCode == 200 || response.statusCode == 201) { + return; + } + final decoded = _tryDecodeJsonMap(response.body); + throw Exception(_extractErrorMessage(decoded, response.statusCode)); + } + /// Déconnexion de l'utilisateur static Future logout() async { await TokenService.clearAll(); diff --git a/frontend/lib/services/reprise_session.dart b/frontend/lib/services/reprise_session.dart new file mode 100644 index 0000000..637d158 --- /dev/null +++ b/frontend/lib/services/reprise_session.dart @@ -0,0 +1,67 @@ +import 'package:p_tits_pas/models/am_registration_data.dart'; +import 'package:p_tits_pas/models/reprise_dossier.dart'; +import 'package:p_tits_pas/models/user_registration_data.dart'; +import 'package:p_tits_pas/services/api/api_config.dart'; + +/// Contexte reprise après refus (token e-mail ou identify). Ticket #112. +class RepriseSession { + RepriseSession._(); + + static String? _token; + static String? _role; + static String? _photoUrl; + + static bool get isActive => + _token != null && _token!.trim().isNotEmpty; + + static String? get token => _token; + + static bool get isParent => _role == 'parent'; + + static bool get isAm => _role == 'assistante_maternelle'; + + static String? get photoUrl => _photoUrl; + + static void start({ + required String token, + required RepriseDossier dossier, + }) { + _token = token.trim(); + _role = dossier.role; + _photoUrl = dossier.photoUrl?.trim().isNotEmpty == true + ? ApiConfig.absoluteMediaUrl(dossier.photoUrl) + : null; + } + + static void clear() { + _token = null; + _role = null; + _photoUrl = null; + } + + static void applyToParent(UserRegistrationData data, RepriseDossier dossier) { + data.resetForReprise( + firstName: dossier.prenom ?? '', + lastName: dossier.nom ?? '', + phone: dossier.telephone ?? '', + email: dossier.email, + address: dossier.adresse ?? '', + postalCode: dossier.codePostal ?? '', + city: dossier.ville ?? '', + ); + } + + static void applyToAm(AmRegistrationData data, RepriseDossier dossier) { + final photo = _photoUrl; + data.resetForReprise( + firstName: dossier.prenom ?? '', + lastName: dossier.nom ?? '', + phone: dossier.telephone ?? '', + email: dossier.email, + streetAddress: dossier.adresse ?? '', + postalCode: dossier.codePostal ?? '', + city: dossier.ville ?? '', + existingPhotoUrl: photo, + ); + } +}