import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'dart:math' as math; import 'dart:io'; import '../models/card_assets.dart'; import '../config/display_config.dart'; import 'custom_app_text_field.dart'; import 'app_custom_checkbox.dart'; import 'hover_relief_widget.dart'; import 'custom_navigation_button.dart'; /// Données pour le formulaire d'informations professionnelles class ProfessionalInfoData { final String? photoPath; final File? photoFile; final bool photoConsent; final DateTime? dateOfBirth; final String birthCity; final String birthCountry; final String nir; final String agrementNumber; final int? capacity; ProfessionalInfoData({ this.photoPath, this.photoFile, this.photoConsent = false, this.dateOfBirth, this.birthCity = '', this.birthCountry = '', this.nir = '', this.agrementNumber = '', this.capacity, }); } /// Widget générique pour le formulaire d'informations professionnelles /// Utilisé pour l'inscription des Assistantes Maternelles /// Supporte mode éditable et readonly, responsive mobile/desktop class ProfessionalInfoFormScreen extends StatefulWidget { final DisplayMode mode; final String stepText; final String title; final CardColorHorizontal cardColor; final ProfessionalInfoData? initialData; final String previousRoute; final Function(ProfessionalInfoData) onSubmit; final Future Function()? onPickPhoto; const ProfessionalInfoFormScreen({ super.key, this.mode = DisplayMode.editable, required this.stepText, required this.title, required this.cardColor, this.initialData, required this.previousRoute, required this.onSubmit, this.onPickPhoto, }); @override State createState() => _ProfessionalInfoFormScreenState(); } class _ProfessionalInfoFormScreenState extends State { final _formKey = GlobalKey(); final _dateOfBirthController = TextEditingController(); final _birthCityController = TextEditingController(); final _birthCountryController = TextEditingController(); final _nirController = TextEditingController(); final _agrementController = TextEditingController(); final _capacityController = TextEditingController(); DateTime? _selectedDate; String? _photoPathFramework; File? _photoFile; bool _photoConsent = false; @override void initState() { super.initState(); final data = widget.initialData; if (data != null) { _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() ?? ''; _photoPathFramework = data.photoPath; _photoFile = data.photoFile; _photoConsent = data.photoConsent; } } @override void dispose() { _dateOfBirthController.dispose(); _birthCityController.dispose(); _birthCountryController.dispose(); _nirController.dispose(); _agrementController.dispose(); _capacityController.dispose(); super.dispose(); } Future _selectDate(BuildContext context) async { final DateTime? picked = await showDatePicker( context: context, initialDate: _selectedDate ?? DateTime.now().subtract(const Duration(days: 365 * 25)), firstDate: DateTime(1920, 1), lastDate: DateTime.now().subtract(const Duration(days: 365 * 18)), locale: const Locale('fr', 'FR'), ); if (picked != null && picked != _selectedDate) { setState(() { _selectedDate = picked; _dateOfBirthController.text = DateFormat('dd/MM/yyyy').format(picked); }); } } Future _pickPhoto() async { if (widget.onPickPhoto != null) { await widget.onPickPhoto!(); } else { // Comportement par défaut : utiliser un asset de test setState(() { _photoPathFramework = 'assets/images/icon_assmat.png'; _photoFile = null; }); } } 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; } final data = ProfessionalInfoData( photoPath: _photoPathFramework, photoFile: _photoFile, photoConsent: _photoConsent, dateOfBirth: _selectedDate, birthCity: _birthCityController.text, birthCountry: _birthCountryController.text, nir: _nirController.text, agrementNumber: _agrementController.text, capacity: int.tryParse(_capacityController.text), ); widget.onSubmit(data); } } @override Widget build(BuildContext context) { final screenSize = MediaQuery.of(context).size; final config = DisplayConfig.fromContext(context, mode: widget.mode); 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( widget.stepText, style: GoogleFonts.merienda( fontSize: config.isMobile ? 13 : 16, color: Colors.black54, ), ), SizedBox(height: config.isMobile ? 6 : 10), Text( widget.title, style: GoogleFonts.merienda( fontSize: config.isMobile ? 18 : 24, fontWeight: FontWeight.bold, color: Colors.black87, ), textAlign: TextAlign.center, ), SizedBox(height: config.isMobile ? 16 : 30), Container( width: config.isMobile ? screenSize.width * 0.9 : screenSize.width * 0.6, padding: EdgeInsets.symmetric( vertical: config.isMobile ? 20 : 50, horizontal: config.isMobile ? 24 : 50, ), decoration: BoxDecoration( image: DecorationImage( image: AssetImage( config.isMobile ? _getVerticalCardAsset() : widget.cardColor.path ), fit: BoxFit.fill, ), ), child: Form( key: _formKey, child: config.isMobile ? _buildMobileFields(context, config) : _buildDesktopFields(context, config), ), ), // Boutons mobile sous la carte if (config.isMobile) ...[ const SizedBox(height: 20), _buildMobileButtons(context, config, screenSize), const SizedBox(height: 10), ], ], ), ), ), // Chevrons desktop uniquement if (!config.isMobile) ...[ // 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: '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', ), ), ], ], ), ); } /// Layout DESKTOP : Photo à gauche, champs à droite Widget _buildDesktopFields(BuildContext context, DisplayConfig config) { return Column( mainAxisSize: MainAxisSize.min, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Photo + Checkbox à gauche SizedBox( width: 300, child: _buildPhotoSection(context, config), ), const SizedBox(width: 30), // Champs à droite Expanded( child: Column( children: [ _buildField( config: config, label: 'Ville de naissance', controller: _birthCityController, hint: 'Votre ville de naissance', validator: (v) => v!.isEmpty ? 'Ville requise' : null, ), const SizedBox(height: 32), _buildField( config: config, label: 'Pays de naissance', controller: _birthCountryController, hint: 'Votre pays de naissance', validator: (v) => v!.isEmpty ? 'Pays requis' : null, ), const SizedBox(height: 32), _buildField( config: config, label: 'Date de naissance', controller: _dateOfBirthController, hint: 'JJ/MM/AAAA', readOnly: true, onTap: () => _selectDate(context), suffixIcon: Icons.calendar_today, validator: (v) => _selectedDate == null ? 'Date requise' : null, ), ], ), ), ], ), const SizedBox(height: 32), _buildField( config: config, label: 'N° Sécurité Sociale (NIR)', controller: _nirController, hint: 'Votre NIR à 13 chiffres', keyboardType: TextInputType.number, validator: (v) { 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'; return null; }, ), const SizedBox(height: 32), Row( children: [ Expanded( child: _buildField( config: config, label: 'N° d\'agrément', controller: _agrementController, hint: 'Votre numéro d\'agrément', validator: (v) => v!.isEmpty ? 'Agrément requis' : null, ), ), const SizedBox(width: 20), Expanded( child: _buildField( config: config, label: 'Capacité d\'accueil', controller: _capacityController, hint: 'Ex: 3', keyboardType: TextInputType.number, 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; }, ), ), ], ), ], ); } /// Layout MOBILE : Tout empilé verticalement Widget _buildMobileFields(BuildContext context, DisplayConfig config) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Photo + Checkbox en premier _buildPhotoSection(context, config), const SizedBox(height: 20), _buildField( config: config, label: 'Ville de naissance', controller: _birthCityController, hint: 'Votre ville de naissance', validator: (v) => v!.isEmpty ? 'Ville requise' : null, ), const SizedBox(height: 12), _buildField( config: config, label: 'Pays de naissance', controller: _birthCountryController, hint: 'Votre pays de naissance', validator: (v) => v!.isEmpty ? 'Pays requis' : null, ), const SizedBox(height: 12), _buildField( config: config, label: 'Date de naissance', controller: _dateOfBirthController, hint: 'JJ/MM/AAAA', readOnly: true, onTap: () => _selectDate(context), suffixIcon: Icons.calendar_today, validator: (v) => _selectedDate == null ? 'Date requise' : null, ), const SizedBox(height: 12), _buildField( config: config, label: 'N° Sécurité Sociale (NIR)', controller: _nirController, hint: 'Votre NIR à 13 chiffres', keyboardType: TextInputType.number, validator: (v) { 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'; return null; }, ), const SizedBox(height: 12), _buildField( config: config, label: 'N° d\'agrément', controller: _agrementController, hint: 'Votre numéro d\'agrément', validator: (v) => v!.isEmpty ? 'Agrément requis' : null, ), const SizedBox(height: 12), _buildField( config: config, label: 'Capacité d\'accueil', controller: _capacityController, hint: 'Ex: 3', keyboardType: TextInputType.number, 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; }, ), ], ); } /// Section photo + checkbox Widget _buildPhotoSection(BuildContext context, DisplayConfig config) { 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!); } final photoSize = config.isMobile ? 200.0 : 270.0; return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ HoverReliefWidget( onPressed: _pickPhoto, borderRadius: BorderRadius.circular(10.0), initialShadowColor: initialPhotoShadow, hoverShadowColor: hoverPhotoShadow, child: SizedBox( height: photoSize, width: photoSize, 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), AppCustomCheckbox( label: 'J\'accepte l\'utilisation\nde ma photo.', value: _photoConsent, onChanged: (val) => setState(() => _photoConsent = val ?? false), ), ], ); } /// Construit un champ individuel Widget _buildField({ required DisplayConfig config, required String label, required TextEditingController controller, String? hint, TextInputType? keyboardType, bool readOnly = false, VoidCallback? onTap, IconData? suffixIcon, String? Function(String?)? validator, }) { return CustomAppTextField( controller: controller, labelText: label, hintText: hint ?? label, fieldWidth: double.infinity, fieldHeight: config.isMobile ? 45.0 : 53.0, labelFontSize: config.isMobile ? 15.0 : 22.0, inputFontSize: config.isMobile ? 14.0 : 20.0, keyboardType: keyboardType ?? TextInputType.text, readOnly: readOnly, onTap: onTap, suffixIcon: suffixIcon, validator: validator, ); } /// Boutons mobile Widget _buildMobileButtons(BuildContext context, DisplayConfig config, Size screenSize) { return 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(widget.previousRoute); } }, width: double.infinity, height: 50, fontSize: 16, ), ), ), const SizedBox(width: 16), Expanded( child: HoverReliefWidget( child: CustomNavigationButton( text: 'Suivant', style: NavigationButtonStyle.green, onPressed: _submitForm, width: double.infinity, height: 50, fontSize: 16, ), ), ), ], ), ); } /// Retourne l'asset de carte vertical correspondant à la couleur String _getVerticalCardAsset() { switch (widget.cardColor) { case CardColorHorizontal.blue: return CardColorVertical.blue.path; case CardColorHorizontal.green: return CardColorVertical.green.path; case CardColorHorizontal.lavender: return CardColorVertical.lavender.path; case CardColorHorizontal.lime: return CardColorVertical.lime.path; case CardColorHorizontal.peach: return CardColorVertical.peach.path; case CardColorHorizontal.pink: return CardColorVertical.pink.path; case CardColorHorizontal.red: return CardColorVertical.red.path; } } }