petitspas/frontend/lib/screens/auth/parent_register_step5_screen.dart
Julien Martin b99745e0fe 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>
2026-06-16 19:19:55 +02:00

364 lines
13 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:go_router/go_router.dart';
import 'dart:math' as math;
import '../../models/user_registration_data.dart';
import '../../models/card_assets.dart';
import '../../config/display_config.dart';
import '../../widgets/hover_relief_widget.dart';
import '../../widgets/image_button.dart';
import '../../widgets/custom_navigation_button.dart';
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';
import '../../utils/reprise_payload.dart';
class ParentRegisterStep5Screen extends StatefulWidget {
const ParentRegisterStep5Screen({super.key});
@override
_ParentRegisterStep5ScreenState createState() => _ParentRegisterStep5ScreenState();
}
class _ParentRegisterStep5ScreenState extends State<ParentRegisterStep5Screen> {
bool _isSubmitting = false;
Future<void> _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, les enfants 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!;
await AuthService.resoumettreReprise(
ReprisePayload.parentPatch(data, token),
);
RepriseSession.clear();
if (!context.mounted) return;
_showRepriseSuccessModal(context);
return;
}
await AuthService.registerParent(data);
if (!context.mounted) return;
_showSuccessModal(context);
} catch (e) {
if (!context.mounted) return;
final msg = e.toString().replaceAll('Exception: ', '');
await showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Envoi impossible', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
content: Text(msg, style: GoogleFonts.merienda(fontSize: 14)),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
),
],
),
);
} finally {
if (mounted) setState(() => _isSubmitting = false);
}
}
@override
Widget build(BuildContext context) {
final registrationData = Provider.of<UserRegistrationData>(context);
final screenSize = MediaQuery.of(context).size;
final config = DisplayConfig.fromContext(context, mode: DisplayMode.readonly);
return Scaffold(
body: Stack(
children: [
Positioned.fill(
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY),
),
Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 40.0),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: config.isMobile ? 0 : screenSize.width / 4.0
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Étape 5/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
const SizedBox(height: 20),
Text('Récapitulatif de votre demande', style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center),
const SizedBox(height: 30),
// Carte Parent 1
_buildParent1(context, registrationData),
const SizedBox(height: 20),
// Carte Parent 2 (si présent)
if (registrationData.parent2 != null) ...[
_buildParent2(context, registrationData),
const SizedBox(height: 20),
],
// Cartes Enfants
...registrationData.children.asMap().entries.map((entry) =>
Column(
children: [
_buildChildCard(context, entry.value, entry.key),
const SizedBox(height: 20),
],
)
),
// Carte Motivation
_buildMotivation(context, registrationData),
const SizedBox(height: 40),
// Boutons Mobile (Retour + Soumettre) ou Bouton Soumettre Desktop
if (config.isMobile)
Padding(
padding: EdgeInsets.symmetric(horizontal: screenSize.width * 0.05),
child: Row(
children: [
Expanded(
child: HoverReliefWidget(
child: CustomNavigationButton(
text: 'Précédent',
style: NavigationButtonStyle.purple,
onPressed: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/parent-register-step4');
}
},
width: double.infinity,
height: 50,
fontSize: 16,
),
),
),
const SizedBox(width: 16),
Expanded(
child: HoverReliefWidget(
child: CustomNavigationButton(
text: _isSubmitting ? 'Envoi…' : 'Soumettre',
style: NavigationButtonStyle.green,
onPressed: _isSubmitting
? () {}
: () => _submitRegistration(context, registrationData),
width: double.infinity,
height: 50,
fontSize: 16,
),
),
),
],
),
)
else
_isSubmitting
? const Padding(
padding: EdgeInsets.all(16),
child: CircularProgressIndicator(),
)
: ImageButton(
bg: 'assets/images/bg_green.png',
text: 'Soumettre ma demande',
textColor: const Color(0xFF2D6A4F),
width: 350,
height: 50,
fontSize: 18,
onPressed: () => _submitRegistration(context, registrationData),
),
],
),
),
),
),
// Chevrons desktop uniquement
if (!config.isMobile)
Positioned(
top: screenSize.height / 2 - 20,
left: 40,
child: IconButton(
icon: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(math.pi),
child: Image.asset('assets/images/chevron_right.png', height: 40),
),
onPressed: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/parent-register-step4');
}
},
tooltip: 'Retour',
),
),
],
),
);
}
Widget _buildParent1(BuildContext context, UserRegistrationData data) {
return PersonalInfoFormScreen(
mode: DisplayMode.readonly,
embedContentOnly: true,
stepText: '',
title: 'Informations du Parent Principal',
cardColor: CardColorHorizontal.peach,
initialData: PersonalInfoData(
firstName: data.parent1.firstName,
lastName: data.parent1.lastName,
phone: data.parent1.phone,
email: data.parent1.email,
address: data.parent1.address,
postalCode: data.parent1.postalCode,
city: data.parent1.city,
),
onSubmit: (d, {hasSecondPerson, sameAddress}) {},
previousRoute: '',
onEdit: () => context.go('/parent-register-step1'),
);
}
Widget _buildParent2(BuildContext context, UserRegistrationData data) {
if (data.parent2 == null) return const SizedBox();
return PersonalInfoFormScreen(
mode: DisplayMode.readonly,
embedContentOnly: true,
stepText: '',
title: 'Informations du Deuxième Parent',
cardColor: CardColorHorizontal.blue,
initialData: PersonalInfoData(
firstName: data.parent2!.firstName,
lastName: data.parent2!.lastName,
phone: data.parent2!.phone,
email: data.parent2!.email,
address: data.parent2!.address,
postalCode: data.parent2!.postalCode,
city: data.parent2!.city,
),
onSubmit: (d, {hasSecondPerson, sameAddress}) {},
previousRoute: '',
onEdit: () => context.go('/parent-register-step2'),
);
}
Widget _buildChildCard(BuildContext context, ChildData child, int index) {
// Note: Le titre est maintenant intégré dans la carte ChildCardWidget en mode readonly
return Column(
children: [
ChildCardWidget(
key: ValueKey('child_readonly_$index'),
childData: child,
childIndex: index,
mode: DisplayMode.readonly,
onPickImage: () {},
onClearImage: () {},
onDateSelect: () {},
onFirstNameChanged: (v) {},
onLastNameChanged: (v) {},
onGenreChanged: (v) {},
onTogglePhotoConsent: (v) {},
onToggleIsUnborn: (v) {},
onRemove: () {},
canBeRemoved: false,
onEdit: () => context.go('/parent-register-step3', extra: {'childIndex': index}),
),
],
);
}
Widget _buildMotivation(BuildContext context, UserRegistrationData data) {
return PresentationFormScreen(
mode: DisplayMode.readonly,
embedContentOnly: true,
stepText: '',
title: 'Votre Motivation',
cardColor: CardColorHorizontal.green, // Changé de pink à green
textFieldHint: '',
initialText: data.motivationText,
initialCguAccepted: data.cguAccepted,
previousRoute: '',
onSubmit: (t, c) {},
onEdit: () => context.go('/parent-register-step4'),
);
}
void _showRepriseSuccessModal(BuildContext context) {
showDialog<void>(
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: <Widget>[
TextButton(
child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
onPressed: () {
Navigator.of(dialogContext).pop();
context.go('/login');
},
),
],
);
},
);
}
void _showSuccessModal(BuildContext context) {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: Text(
'Demande enregistrée',
style: GoogleFonts.merienda(fontWeight: FontWeight.bold),
),
content: Text(
'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.',
style: GoogleFonts.merienda(fontSize: 14),
),
actions: <Widget>[
TextButton(
child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)),
onPressed: () {
Navigator.of(dialogContext).pop();
context.go('/login');
},
),
],
);
},
);
}
}