diff --git a/frontend/lib/widgets/personal_info_form_screen.dart b/frontend/lib/widgets/personal_info_form_screen.dart new file mode 100644 index 0000000..5a90d57 --- /dev/null +++ b/frontend/lib/widgets/personal_info_form_screen.dart @@ -0,0 +1,380 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:go_router/go_router.dart'; +import 'dart:math' as math; + +import 'custom_app_text_field.dart'; +import 'app_custom_checkbox.dart'; +import '../models/card_assets.dart'; + +/// Modèle de données pour le formulaire +class PersonalInfoData { + String firstName; + String lastName; + String phone; + String email; + String address; + String postalCode; + String city; + + PersonalInfoData({ + this.firstName = '', + this.lastName = '', + this.phone = '', + this.email = '', + this.address = '', + this.postalCode = '', + this.city = '', + }); +} + +/// Widget générique pour les formulaires d'informations personnelles +class PersonalInfoFormScreen extends StatefulWidget { + final String stepText; // Ex: "Étape 1/5" + final String title; // Ex: "Informations du Parent Principal" + final CardColorHorizontal cardColor; + final PersonalInfoData initialData; + final Function(PersonalInfoData data, {bool? hasSecondPerson, bool? sameAddress}) onSubmit; + final String previousRoute; + + // Options spécifiques pour Parent 2 + final bool showSecondPersonToggle; // Afficher "Il y a un 2ème parent" + final bool? initialHasSecondPerson; + final bool showSameAddressCheckbox; // Afficher "Même adresse que parent 1" + final bool? initialSameAddress; + final PersonalInfoData? referenceAddressData; // Pour pré-remplir si "même adresse" + + const PersonalInfoFormScreen({ + super.key, + required this.stepText, + required this.title, + required this.cardColor, + required this.initialData, + required this.onSubmit, + required this.previousRoute, + this.showSecondPersonToggle = false, + this.initialHasSecondPerson, + this.showSameAddressCheckbox = false, + this.initialSameAddress, + this.referenceAddressData, + }); + + @override + State createState() => _PersonalInfoFormScreenState(); +} + +class _PersonalInfoFormScreenState extends State { + final _formKey = GlobalKey(); + late TextEditingController _lastNameController; + late TextEditingController _firstNameController; + late TextEditingController _phoneController; + late TextEditingController _emailController; + late TextEditingController _addressController; + late TextEditingController _postalCodeController; + late TextEditingController _cityController; + + bool _hasSecondPerson = false; + bool _sameAddress = false; + bool _fieldsEnabled = true; + + @override + void initState() { + super.initState(); + _lastNameController = TextEditingController(text: widget.initialData.lastName); + _firstNameController = TextEditingController(text: widget.initialData.firstName); + _phoneController = TextEditingController(text: widget.initialData.phone); + _emailController = TextEditingController(text: widget.initialData.email); + _addressController = TextEditingController(text: widget.initialData.address); + _postalCodeController = TextEditingController(text: widget.initialData.postalCode); + _cityController = TextEditingController(text: widget.initialData.city); + + if (widget.showSecondPersonToggle) { + _hasSecondPerson = widget.initialHasSecondPerson ?? true; + _fieldsEnabled = _hasSecondPerson; + } + + if (widget.showSameAddressCheckbox) { + _sameAddress = widget.initialSameAddress ?? false; + _updateAddressFields(); + } + } + + @override + void dispose() { + _lastNameController.dispose(); + _firstNameController.dispose(); + _phoneController.dispose(); + _emailController.dispose(); + _addressController.dispose(); + _postalCodeController.dispose(); + _cityController.dispose(); + super.dispose(); + } + + void _updateAddressFields() { + if (_sameAddress && widget.referenceAddressData != null) { + _addressController.text = widget.referenceAddressData!.address; + _postalCodeController.text = widget.referenceAddressData!.postalCode; + _cityController.text = widget.referenceAddressData!.city; + } + } + + void _handleSubmit() { + if (_formKey.currentState!.validate()) { + final data = PersonalInfoData( + firstName: _firstNameController.text, + lastName: _lastNameController.text, + phone: _phoneController.text, + email: _emailController.text, + address: _addressController.text, + postalCode: _postalCodeController.text, + city: _cityController.text, + ); + + widget.onSubmit( + data, + hasSecondPerson: widget.showSecondPersonToggle ? _hasSecondPerson : null, + sameAddress: widget.showSameAddressCheckbox ? _sameAddress : null, + ); + } + } + + @override + Widget build(BuildContext 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.repeat), + ), + Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 40.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(stepText, style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)), + const SizedBox(height: 10), + Text( + widget.title, + style: GoogleFonts.merienda( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 30), + + // Toggle "Il y a un 2ème parent" (uniquement pour Parent 2) + if (widget.showSecondPersonToggle) ...[ + Padding( + padding: EdgeInsets.symmetric(horizontal: screenSize.width * 0.2), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Il y a un 2ème parent ?', + style: GoogleFonts.merienda(fontSize: 18, fontWeight: FontWeight.w600), + ), + const SizedBox(width: 15), + Switch( + value: _hasSecondPerson, + onChanged: (value) { + setState(() { + _hasSecondPerson = value; + _fieldsEnabled = value; + }); + }, + activeColor: Theme.of(context).primaryColor, + ), + ], + ), + ), + const SizedBox(height: 20), + ], + + Container( + width: screenSize.width * 0.6, + padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50), + constraints: const BoxConstraints(minHeight: 570), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.cardColor.path), + fit: BoxFit.fill, + ), + ), + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + flex: 12, + child: CustomAppTextField( + controller: _lastNameController, + labelText: 'Nom', + hintText: 'Votre nom de famille', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled, + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 12, + child: CustomAppTextField( + controller: _firstNameController, + labelText: 'Prénom', + hintText: 'Votre prénom', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled, + ), + ), + ], + ), + const SizedBox(height: 32), + Row( + children: [ + Expanded( + flex: 12, + child: CustomAppTextField( + controller: _phoneController, + labelText: 'Téléphone', + keyboardType: TextInputType.phone, + hintText: 'Votre numéro de téléphone', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled, + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 12, + child: CustomAppTextField( + controller: _emailController, + labelText: 'Email', + keyboardType: TextInputType.emailAddress, + hintText: 'Votre adresse e-mail', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled, + ), + ), + ], + ), + const SizedBox(height: 32), + + // Checkbox "Même adresse" (uniquement pour Parent 2) + if (widget.showSameAddressCheckbox) ...[ + AppCustomCheckbox( + label: 'Même adresse que le parent 1', + value: _sameAddress, + onChanged: _fieldsEnabled ? (value) { + setState(() { + _sameAddress = value ?? false; + _updateAddressFields(); + }); + } : null, + ), + const SizedBox(height: 20), + ], + + CustomAppTextField( + controller: _addressController, + labelText: 'Adresse (N° et Rue)', + hintText: 'Numéro et nom de votre rue', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled && !_sameAddress, + ), + const SizedBox(height: 32), + Row( + children: [ + Expanded( + flex: 1, + child: CustomAppTextField( + controller: _postalCodeController, + labelText: 'Code Postal', + keyboardType: TextInputType.number, + hintText: 'Code postal', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled && !_sameAddress, + ), + ), + const Expanded(flex: 1, child: SizedBox()), + Expanded( + flex: 4, + child: CustomAppTextField( + controller: _cityController, + labelText: 'Ville', + hintText: 'Votre ville', + style: CustomAppTextFieldStyle.beige, + fieldWidth: double.infinity, + labelFontSize: 22.0, + inputFontSize: 20.0, + enabled: _fieldsEnabled && !_sameAddress, + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + ), + // Chevrons + 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(widget.previousRoute); + } + }, + tooltip: 'Retour', + ), + ), + Positioned( + top: screenSize.height / 2 - 20, + right: 40, + child: IconButton( + icon: Image.asset('assets/images/chevron_right.png', height: 40), + onPressed: _handleSubmit, + tooltip: 'Suivant', + ), + ), + ], + ), + ); + } +} diff --git a/frontend/lib/widgets/presentation_form_screen.dart b/frontend/lib/widgets/presentation_form_screen.dart new file mode 100644 index 0000000..8008f32 --- /dev/null +++ b/frontend/lib/widgets/presentation_form_screen.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:go_router/go_router.dart'; +import 'dart:math' as math; + +import 'custom_decorated_text_field.dart'; +import 'app_custom_checkbox.dart'; +import '../models/card_assets.dart'; + +class PresentationFormScreen extends StatefulWidget { + final String stepText; // Ex: "Étape 3/4" ou "Étape 4/5" + final String title; // Ex: "Présentation et Conditions" ou "Motivation de votre demande" + final CardColorHorizontal cardColor; + final String textFieldHint; + final String initialText; + final bool initialCguAccepted; + final String previousRoute; + final Function(String text, bool cguAccepted) onSubmit; + + const PresentationFormScreen({ + super.key, + required this.stepText, + required this.title, + required this.cardColor, + required this.textFieldHint, + required this.initialText, + required this.initialCguAccepted, + required this.previousRoute, + required this.onSubmit, + }); + + @override + State createState() => _PresentationFormScreenState(); +} + +class _PresentationFormScreenState extends State { + late TextEditingController _textController; + late bool _cguAccepted; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(text: widget.initialText); + _cguAccepted = widget.initialCguAccepted; + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } + + void _handleSubmit() { + if (!_cguAccepted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Vous devez accepter les CGU pour continuer.'), + backgroundColor: Colors.red, + ), + ); + return; + } + widget.onSubmit(_textController.text, _cguAccepted); + } + + @override + Widget build(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + final cardWidth = screenSize.width * 0.6; + final double imageAspectRatio = 2.0; + final cardHeight = cardWidth / imageAspectRatio; + + 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, horizontal: 50.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + widget.stepText, + style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54), + ), + const SizedBox(height: 20), + Text( + widget.title, + style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), + textAlign: TextAlign.center, + ), + const SizedBox(height: 30), + Container( + width: cardWidth, + height: cardHeight, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.cardColor.path), + fit: BoxFit.fill, + ), + ), + child: Padding( + padding: const EdgeInsets.all(40.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: CustomDecoratedTextField( + controller: _textController, + hintText: widget.textFieldHint, + fieldHeight: cardHeight * 0.6, + maxLines: 10, + expandDynamically: true, + fontSize: 18.0, + ), + ), + const SizedBox(height: 20), + AppCustomCheckbox( + label: 'J\'accepte les Conditions Générales\nd\'Utilisation et la Politique de confidentialité', + value: _cguAccepted, + onChanged: (value) => setState(() => _cguAccepted = value ?? false), + ), + ], + ), + ), + ), + ], + ), + ), + ), + // 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(widget.previousRoute); + } + }, + tooltip: 'Retour', + ), + ), + // Chevron Droit (Suivant) + Positioned( + top: screenSize.height / 2 - 20, + right: 40, + child: IconButton( + icon: Image.asset('assets/images/chevron_right.png', height: 40), + onPressed: _cguAccepted ? _handleSubmit : null, + tooltip: 'Suivant', + ), + ), + ], + ), + ); + } +}