feat(auth): Renommer "Nanny" en "Assistante Maternelle" (AM)
- Création du modèle am_registration_data.dart - Création des 4 écrans d'inscription AM (steps 1-4) - Mise à jour du bouton "Assistante Maternelle" dans register_choice - Conformité CDC : pas de champs mot de passe dans les formulaires - Préremplissage des données de test pour faciliter le développement Ref: Ticket #XX - Renommage workflow inscription AM
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../models/am_registration_data.dart';
|
||||
import '../../utils/data_generator.dart';
|
||||
import '../../widgets/personal_info_form_screen.dart';
|
||||
import '../../models/card_assets.dart';
|
||||
|
||||
class AmRegisterStep1Screen extends StatelessWidget {
|
||||
const AmRegisterStep1Screen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final registrationData = Provider.of<AmRegistrationData>(context, listen: false);
|
||||
|
||||
// Générer des données de test si vide
|
||||
PersonalInfoData initialData;
|
||||
if (registrationData.firstName.isEmpty) {
|
||||
final genFirstName = DataGenerator.firstName();
|
||||
final genLastName = DataGenerator.lastName();
|
||||
initialData = PersonalInfoData(
|
||||
firstName: genFirstName,
|
||||
lastName: genLastName,
|
||||
phone: DataGenerator.phone(),
|
||||
email: DataGenerator.email(genFirstName, genLastName),
|
||||
address: DataGenerator.address(),
|
||||
postalCode: DataGenerator.postalCode(),
|
||||
city: DataGenerator.city(),
|
||||
);
|
||||
} else {
|
||||
initialData = PersonalInfoData(
|
||||
firstName: registrationData.firstName,
|
||||
lastName: registrationData.lastName,
|
||||
phone: registrationData.phone,
|
||||
email: registrationData.email,
|
||||
address: registrationData.streetAddress,
|
||||
postalCode: registrationData.postalCode,
|
||||
city: registrationData.city,
|
||||
);
|
||||
}
|
||||
|
||||
return PersonalInfoFormScreen(
|
||||
stepText: 'Étape 1/4',
|
||||
title: 'Vos informations personnelles',
|
||||
cardColor: CardColorHorizontal.blue,
|
||||
initialData: initialData,
|
||||
previousRoute: '/register-choice',
|
||||
onSubmit: (data, {hasSecondPerson, sameAddress}) {
|
||||
registrationData.updatePersonalInfo(
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
phone: data.phone,
|
||||
email: data.email,
|
||||
streetAddress: data.address,
|
||||
postalCode: data.postalCode,
|
||||
city: data.city,
|
||||
password: '',
|
||||
photoConsent: false,
|
||||
);
|
||||
context.go('/am-register-step2');
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'dart:math' as math;
|
||||
import 'dart:io'; // Pour FileImage si _pickPhoto utilise un File
|
||||
|
||||
import '../../models/am_registration_data.dart';
|
||||
import '../../widgets/custom_app_text_field.dart';
|
||||
import '../../widgets/app_custom_checkbox.dart'; // Import de la checkbox
|
||||
import '../../widgets/hover_relief_widget.dart'; // Import du HoverReliefWidget
|
||||
import '../../models/card_assets.dart';
|
||||
import '../../utils/data_generator.dart';
|
||||
|
||||
class AmRegisterStep2Screen extends StatefulWidget {
|
||||
const AmRegisterStep2Screen({super.key});
|
||||
|
||||
@override
|
||||
State<AmRegisterStep2Screen> createState() => _AmRegisterStep2ScreenState();
|
||||
}
|
||||
|
||||
class _AmRegisterStep2ScreenState extends State<AmRegisterStep2Screen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
final _dateOfBirthController = TextEditingController();
|
||||
// final _placeOfBirthController = TextEditingController(); // Remplacé
|
||||
final _birthCityController = TextEditingController(); // Nouveau
|
||||
final _birthCountryController = TextEditingController(); // Nouveau
|
||||
final _nirController = TextEditingController();
|
||||
final _agrementController = TextEditingController();
|
||||
final _capacityController = TextEditingController();
|
||||
DateTime? _selectedDate;
|
||||
String? _photoPathFramework; // Pour stocker le chemin de la photo (Asset ou File path)
|
||||
File? _photoFile; // Pour stocker le fichier image si sélectionné localement
|
||||
bool _photoConsent = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final data = Provider.of<AmRegistrationData>(context, listen: false);
|
||||
_selectedDate = data.dateOfBirth;
|
||||
_dateOfBirthController.text = data.dateOfBirth != null ? DateFormat('dd/MM/yyyy').format(data.dateOfBirth!) : '';
|
||||
_birthCityController.text = data.birthCity;
|
||||
_birthCountryController.text = data.birthCountry;
|
||||
_nirController.text = data.nir;
|
||||
_agrementController.text = data.agrementNumber;
|
||||
_capacityController.text = data.capacity?.toString() ?? '';
|
||||
|
||||
// Générer des données de test si les champs sont vides
|
||||
if (data.dateOfBirth == null && data.nir.isEmpty) {
|
||||
_selectedDate = DateTime(1985, 3, 15);
|
||||
_dateOfBirthController.text = DateFormat('dd/MM/yyyy').format(_selectedDate!);
|
||||
_birthCityController.text = DataGenerator.city();
|
||||
_birthCountryController.text = 'France';
|
||||
_nirController.text = '${DataGenerator.randomIntInRange(1, 3)}${DataGenerator.randomIntInRange(80, 96)}${DataGenerator.randomIntInRange(1, 13).toString().padLeft(2, '0')}${DataGenerator.randomIntInRange(1, 100).toString().padLeft(2, '0')}${DataGenerator.randomIntInRange(100, 1000).toString().padLeft(3, '0')}${DataGenerator.randomIntInRange(100, 1000).toString().padLeft(3, '0')}${DataGenerator.randomIntInRange(10, 100).toString().padLeft(2, '0')}';
|
||||
_agrementController.text = 'AM${DataGenerator.randomIntInRange(10000, 100000)}';
|
||||
_capacityController.text = DataGenerator.randomIntInRange(1, 5).toString();
|
||||
_photoPathFramework = 'assets/images/icon_assmat.png';
|
||||
_photoConsent = true;
|
||||
}
|
||||
|
||||
// Gérer la photo existante (pourrait être un path d'asset ou un path de fichier)
|
||||
if (data.photoPath != null) {
|
||||
if (data.photoPath!.startsWith('assets/')) {
|
||||
_photoPathFramework = data.photoPath;
|
||||
_photoFile = null;
|
||||
} else {
|
||||
_photoFile = File(data.photoPath!);
|
||||
_photoPathFramework = data.photoPath; // ou _photoFile.path
|
||||
}
|
||||
}
|
||||
_photoConsent = data.photoConsent;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_dateOfBirthController.dispose();
|
||||
_birthCityController.dispose();
|
||||
_birthCountryController.dispose();
|
||||
_nirController.dispose();
|
||||
_agrementController.dispose();
|
||||
_capacityController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _selectDate(BuildContext context) async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _selectedDate ?? DateTime.now().subtract(const Duration(days: 365 * 25)), // Default à 25 ans si null
|
||||
firstDate: DateTime(1920, 1),
|
||||
lastDate: DateTime.now().subtract(const Duration(days: 365 * 18)), // Assurer un âge minimum de 18 ans
|
||||
locale: const Locale('fr', 'FR'),
|
||||
);
|
||||
if (picked != null && picked != _selectedDate) {
|
||||
setState(() {
|
||||
_selectedDate = picked;
|
||||
_dateOfBirthController.text = DateFormat('dd/MM/yyyy').format(picked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pickPhoto() async {
|
||||
// TODO: Remplacer par la vraie logique ImagePicker
|
||||
// final imagePicker = ImagePicker();
|
||||
// final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
|
||||
// if (pickedFile != null) {
|
||||
// setState(() {
|
||||
// _photoFile = File(pickedFile.path);
|
||||
// _photoPathFramework = pickedFile.path; // pour la sauvegarde
|
||||
// });
|
||||
// } else {
|
||||
// // Simuler la sélection d'un asset pour test si aucun fichier n'est choisi
|
||||
setState(() {
|
||||
_photoPathFramework = 'assets/images/icon_assmat.png'; // Simule une photo asset
|
||||
_photoFile = null; // Assurez-vous que _photoFile est null si c'est un asset
|
||||
});
|
||||
// }
|
||||
print("Photo sélectionnée: $_photoPathFramework");
|
||||
}
|
||||
|
||||
void _submitForm() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (_photoPathFramework != null && !_photoConsent) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Veuillez accepter le consentement photo pour continuer.')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Provider.of<AmRegistrationData>(context, listen: false)
|
||||
.updateProfessionalInfo(
|
||||
photoPath: _photoPathFramework, // Sauvegarder le chemin (asset ou fichier)
|
||||
photoConsent: _photoConsent,
|
||||
dateOfBirth: _selectedDate,
|
||||
birthCity: _birthCityController.text,
|
||||
birthCountry: _birthCountryController.text,
|
||||
nir: _nirController.text,
|
||||
agrementNumber: _agrementController.text,
|
||||
capacity: int.tryParse(_capacityController.text)
|
||||
);
|
||||
context.go('/am-register-step3');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
const cardColor = CardColorHorizontal.green; // Couleur de la carte
|
||||
final Color baseCardColorForShadow = Colors.green.shade300;
|
||||
final Color initialPhotoShadow = baseCardColorForShadow.withAlpha(90);
|
||||
final Color hoverPhotoShadow = baseCardColorForShadow.withAlpha(130);
|
||||
|
||||
ImageProvider? currentImageProvider;
|
||||
if (_photoFile != null) {
|
||||
currentImageProvider = FileImage(_photoFile!);
|
||||
} else if (_photoPathFramework != null && _photoPathFramework!.startsWith('assets/')) {
|
||||
currentImageProvider = AssetImage(_photoPathFramework!);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat),
|
||||
),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Étape 2/4', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Vos informations professionnelles',
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Container(
|
||||
width: screenSize.width * 0.6,
|
||||
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50),
|
||||
constraints: const BoxConstraints(minHeight: 650),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(image: AssetImage(cardColor.path), fit: BoxFit.fill),
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Colonne Gauche: Photo et Checkbox
|
||||
SizedBox(
|
||||
width: 300, // Largeur fixe pour la colonne photo (200 * 1.5)
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center, // Centrer les éléments horizontalement
|
||||
children: [
|
||||
HoverReliefWidget(
|
||||
onPressed: _pickPhoto,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
initialShadowColor: initialPhotoShadow,
|
||||
hoverShadowColor: hoverPhotoShadow,
|
||||
child: SizedBox(
|
||||
height: 270, // (180 * 1.5)
|
||||
width: 270, // (180 * 1.5)
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: currentImageProvider != null
|
||||
? DecorationImage(image: currentImageProvider, fit: BoxFit.cover)
|
||||
: null,
|
||||
),
|
||||
child: currentImageProvider == null
|
||||
? Image.asset('assets/images/photo.png', fit: BoxFit.contain)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10), // Espace réduit
|
||||
AppCustomCheckbox(
|
||||
label: 'J\'accepte l\'utilisation\nde ma photo.',
|
||||
value: _photoConsent,
|
||||
onChanged: (val) => setState(() => _photoConsent = val ?? false),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 30), // Augmenter l'espace entre les colonnes
|
||||
// Colonne Droite: Champs de naissance
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
CustomAppTextField(
|
||||
controller: _birthCityController,
|
||||
labelText: 'Ville de naissance',
|
||||
hintText: 'Votre ville de naissance',
|
||||
fieldWidth: double.infinity,
|
||||
validator: (v) => v!.isEmpty ? 'Ville requise' : null,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CustomAppTextField(
|
||||
controller: _birthCountryController,
|
||||
labelText: 'Pays de naissance',
|
||||
hintText: 'Votre pays de naissance',
|
||||
fieldWidth: double.infinity,
|
||||
validator: (v) => v!.isEmpty ? 'Pays requis' : null,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CustomAppTextField(
|
||||
controller: _dateOfBirthController,
|
||||
labelText: 'Date de naissance',
|
||||
hintText: 'JJ/MM/AAAA',
|
||||
readOnly: true,
|
||||
onTap: () => _selectDate(context),
|
||||
suffixIcon: Icons.calendar_today, // Assurez-vous que CustomAppTextField gère suffixIcon
|
||||
fieldWidth: double.infinity,
|
||||
validator: (v) => _selectedDate == null ? 'Date requise' : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CustomAppTextField(
|
||||
controller: _nirController,
|
||||
labelText: 'N° Sécurité Sociale (NIR)',
|
||||
hintText: 'Votre NIR à 13 chiffres',
|
||||
keyboardType: TextInputType.number,
|
||||
fieldWidth: double.infinity,
|
||||
validator: (v) { // Validation plus précise du NIR
|
||||
if (v == null || v.isEmpty) return 'NIR requis';
|
||||
if (v.length != 13) return 'Le NIR doit contenir 13 chiffres';
|
||||
if (!RegExp(r'^[1-3]').hasMatch(v[0])) return 'Le NIR doit commencer par 1, 2 ou 3';
|
||||
// D'autres validations plus complexes (clé de contrôle) peuvent être ajoutées
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomAppTextField(
|
||||
controller: _agrementController,
|
||||
labelText: 'N° d\'agrément',
|
||||
hintText: 'Votre numéro d\'agrément',
|
||||
fieldWidth: double.infinity,
|
||||
validator: (v) => v!.isEmpty ? 'Agrément requis' : null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: CustomAppTextField(
|
||||
controller: _capacityController,
|
||||
labelText: 'Capacité d\'accueil',
|
||||
hintText: 'Ex: 3',
|
||||
keyboardType: TextInputType.number,
|
||||
fieldWidth: double.infinity,
|
||||
validator: (v) {
|
||||
if (v == null || v.isEmpty) return 'Capacité requise';
|
||||
final n = int.tryParse(v);
|
||||
if (n == null || n <= 0) return 'Nombre invalide';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Chevron Gauche (Retour)
|
||||
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('/am-register-step1');
|
||||
}
|
||||
},
|
||||
tooltip: 'Précédent',
|
||||
),
|
||||
),
|
||||
// Chevron Droit (Suivant)
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
right: 40,
|
||||
child: IconButton(
|
||||
icon: Image.asset('assets/images/chevron_right.png', height: 40),
|
||||
onPressed: _submitForm,
|
||||
tooltip: 'Suivant',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../models/am_registration_data.dart';
|
||||
import '../../widgets/presentation_form_screen.dart';
|
||||
import '../../models/card_assets.dart';
|
||||
|
||||
class AmRegisterStep3Screen extends StatelessWidget {
|
||||
const AmRegisterStep3Screen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final data = Provider.of<AmRegistrationData>(context, listen: false);
|
||||
|
||||
// Générer un texte de test si vide
|
||||
String initialText = data.presentationText;
|
||||
bool initialCgu = data.cguAccepted;
|
||||
|
||||
if (initialText.isEmpty) {
|
||||
initialText = 'Disponible immédiatement, plus de 10 ans d\'expérience avec les tout-petits. Formation aux premiers secours à jour. Je dispose d\'un jardin sécurisé et d\'un espace de jeu adapté.';
|
||||
initialCgu = true;
|
||||
}
|
||||
|
||||
return PresentationFormScreen(
|
||||
stepText: 'Étape 3/4',
|
||||
title: 'Présentation et Conditions',
|
||||
cardColor: CardColorHorizontal.peach,
|
||||
textFieldHint: 'Ex: Disponible immédiatement, 10 ans d\'expérience, formation premiers secours...',
|
||||
initialText: initialText,
|
||||
initialCguAccepted: initialCgu,
|
||||
previousRoute: '/am-register-step2',
|
||||
onSubmit: (text, cguAccepted) {
|
||||
data.updatePresentationAndCgu(
|
||||
presentationText: text,
|
||||
cguAccepted: cguAccepted,
|
||||
);
|
||||
context.go('/am-register-step4');
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import '../../models/am_registration_data.dart';
|
||||
import '../../widgets/image_button.dart';
|
||||
import '../../models/card_assets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
// Méthode helper pour afficher un champ de type "lecture seule" stylisé
|
||||
Widget _buildDisplayFieldValue(BuildContext context, String label, String value, {bool multiLine = false, double fieldHeight = 50.0, double labelFontSize = 18.0}) {
|
||||
const FontWeight labelFontWeight = FontWeight.w600;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)),
|
||||
const SizedBox(height: 4),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: multiLine ? null : fieldHeight,
|
||||
constraints: multiLine ? const BoxConstraints(minHeight: 50.0) : null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/input_field_bg.png'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
value.isNotEmpty ? value : '-',
|
||||
style: GoogleFonts.merienda(fontSize: labelFontSize),
|
||||
maxLines: multiLine ? null : 1,
|
||||
overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class AmRegisterStep4Screen extends StatefulWidget {
|
||||
const AmRegisterStep4Screen({super.key});
|
||||
|
||||
@override
|
||||
_AmRegisterStep4ScreenState createState() => _AmRegisterStep4ScreenState();
|
||||
}
|
||||
|
||||
class _AmRegisterStep4ScreenState extends State<AmRegisterStep4Screen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final registrationData = Provider.of<AmRegistrationData>(context);
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
|
||||
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: screenSize.width / 4.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text('Étape 4/4', 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),
|
||||
|
||||
_buildPersonalInfoCard(context, registrationData),
|
||||
const SizedBox(height: 20),
|
||||
_buildProfessionalInfoCard(context, registrationData),
|
||||
const SizedBox(height: 20),
|
||||
_buildPresentationCard(context, registrationData),
|
||||
const SizedBox(height: 40),
|
||||
|
||||
ImageButton(
|
||||
bg: 'assets/images/btn_green.png',
|
||||
text: 'Soumettre ma demande',
|
||||
textColor: const Color(0xFF2D6A4F),
|
||||
width: 350,
|
||||
height: 50,
|
||||
fontSize: 18,
|
||||
onPressed: () {
|
||||
print("Données AM finales: ${registrationData.firstName} ${registrationData.lastName}");
|
||||
_showConfirmationModal(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: screenSize.height / 2 - 20,
|
||||
left: 40,
|
||||
child: IconButton(
|
||||
icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)),
|
||||
onPressed: () {
|
||||
if (context.canPop()) {
|
||||
context.pop();
|
||||
} else {
|
||||
context.go('/am-register-step3');
|
||||
}
|
||||
},
|
||||
tooltip: 'Retour',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showConfirmationModal(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');
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Carte Informations personnelles
|
||||
Widget _buildPersonalInfoCard(BuildContext context, AmRegistrationData data) {
|
||||
const double verticalSpacing = 28.0;
|
||||
const double labelFontSize = 22.0;
|
||||
|
||||
List<Widget> details = [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Nom:", data.lastName, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Prénom:", data.firstName, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Téléphone:", data.phone, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Email:", data.email, multiLine: true, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
_buildDisplayFieldValue(context, "Adresse:", "${data.streetAddress}\n${data.postalCode} ${data.city}".trim(), multiLine: true, fieldHeight: 80, labelFontSize: labelFontSize),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
_buildDisplayFieldValue(context, "Consentement photo:", data.photoConsent ? "Oui" : "Non", labelFontSize: labelFontSize),
|
||||
];
|
||||
|
||||
return _SummaryCard(
|
||||
backgroundImagePath: CardColorHorizontal.blue.path,
|
||||
title: 'Informations personnelles',
|
||||
content: details,
|
||||
onEdit: () => context.go('/am-register-step1'),
|
||||
);
|
||||
}
|
||||
|
||||
// Carte Informations professionnelles
|
||||
Widget _buildProfessionalInfoCard(BuildContext context, AmRegistrationData data) {
|
||||
const double verticalSpacing = 28.0;
|
||||
const double labelFontSize = 22.0;
|
||||
|
||||
String formattedDate = '-';
|
||||
if (data.dateOfBirth != null) {
|
||||
formattedDate = '${data.dateOfBirth!.day.toString().padLeft(2, '0')}/${data.dateOfBirth!.month.toString().padLeft(2, '0')}/${data.dateOfBirth!.year}';
|
||||
}
|
||||
String birthPlace = '${data.birthCity}, ${data.birthCountry}'.trim();
|
||||
if (birthPlace == ',') birthPlace = '-';
|
||||
|
||||
List<Widget> details = [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Date de naissance:", formattedDate, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "Lieu de naissance:", birthPlace, labelFontSize: labelFontSize, multiLine: true)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: _buildDisplayFieldValue(context, "N° Sécurité Sociale:", data.nir, labelFontSize: labelFontSize)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: _buildDisplayFieldValue(context, "N° Agrément:", data.agrementNumber, labelFontSize: labelFontSize)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: verticalSpacing),
|
||||
_buildDisplayFieldValue(context, "Capacité d'accueil:", data.capacity?.toString() ?? '-', labelFontSize: labelFontSize),
|
||||
];
|
||||
|
||||
return _SummaryCard(
|
||||
backgroundImagePath: CardColorHorizontal.green.path,
|
||||
title: 'Informations professionnelles',
|
||||
content: details,
|
||||
onEdit: () => context.go('/am-register-step2'),
|
||||
);
|
||||
}
|
||||
|
||||
// Carte Présentation & CGU
|
||||
Widget _buildPresentationCard(BuildContext context, AmRegistrationData data) {
|
||||
const double labelFontSize = 22.0;
|
||||
|
||||
List<Widget> details = [
|
||||
Text(
|
||||
'Votre présentation (facultatif) :',
|
||||
style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
constraints: const BoxConstraints(minHeight: 80.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/input_field_bg.png'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
data.presentationText.isNotEmpty ? data.presentationText : 'Aucune présentation rédigée.',
|
||||
style: GoogleFonts.merienda(
|
||||
fontSize: 18,
|
||||
fontStyle: data.presentationText.isNotEmpty ? FontStyle.normal : FontStyle.italic,
|
||||
color: data.presentationText.isNotEmpty ? Colors.black87 : Colors.black54,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
data.cguAccepted ? Icons.check_circle : Icons.cancel,
|
||||
color: data.cguAccepted ? Colors.green : Colors.red,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
data.cguAccepted ? 'CGU acceptées' : 'CGU non acceptées',
|
||||
style: GoogleFonts.merienda(fontSize: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
return _SummaryCard(
|
||||
backgroundImagePath: CardColorHorizontal.peach.path,
|
||||
title: 'Présentation & CGU',
|
||||
content: details,
|
||||
onEdit: () => context.go('/am-register-step3'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Widget générique _SummaryCard
|
||||
class _SummaryCard extends StatelessWidget {
|
||||
final String backgroundImagePath;
|
||||
final String title;
|
||||
final List<Widget> content;
|
||||
final VoidCallback onEdit;
|
||||
|
||||
const _SummaryCard({
|
||||
super.key,
|
||||
required this.backgroundImagePath,
|
||||
required this.title,
|
||||
required this.content,
|
||||
required this.onEdit,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AspectRatio(
|
||||
aspectRatio: 2.0,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(backgroundImagePath),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
title,
|
||||
style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...content,
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit, color: Colors.black54, size: 28),
|
||||
onPressed: onEdit,
|
||||
tooltip: 'Modifier',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ class RegisterChoiceScreen extends StatelessWidget {
|
||||
iconPath: 'assets/images/icon_assmat.png',
|
||||
label: 'Assistante Maternelle',
|
||||
onPressed: () {
|
||||
context.go('/nanny-register-step1');
|
||||
context.go('/am-register-step1');
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user