import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../bloc/onboarding_bloc.dart'; import '../../data/models/formule_model.dart'; import '../../../../shared/design_system/tokens/unionflow_colors.dart'; import 'onboarding_shared_widgets.dart'; /// Étape 1 — Choix de la taille de l'organisation et du niveau de formule class PlanSelectionPage extends StatefulWidget { final List formules; const PlanSelectionPage({super.key, required this.formules}); @override State createState() => _PlanSelectionPageState(); } class _PlanSelectionPageState extends State { String? _selectedPlage; String? _selectedFormule; static const _plages = [ _Plage('PETITE', 'Petite', '1 – 100 membres', Icons.group_outlined, 'Associations naissantes et petites structures'), _Plage('MOYENNE', 'Moyenne', '101 – 500 membres', Icons.groups_outlined, 'Associations établies en croissance'), _Plage('GRANDE', 'Grande', '501 – 2 000 membres', Icons.corporate_fare_outlined, 'Grandes organisations régionales'), _Plage('TRES_GRANDE', 'Très grande', '2 000+ membres', Icons.account_balance_outlined, 'Fédérations et réseaux nationaux'), ]; static const _formuleColors = { 'BASIC': UnionFlowColors.unionGreen, 'STANDARD': UnionFlowColors.gold, 'PREMIUM': UnionFlowColors.indigo, }; static const _formuleIcons = { 'BASIC': Icons.star_border_rounded, 'STANDARD': Icons.star_half_rounded, 'PREMIUM': Icons.star_rounded, }; static const _formuleFeatures = { 'BASIC': ['Gestion des membres', 'Cotisations de base', 'Rapports mensuels', 'Support email'], 'STANDARD': ['Tout Basic +', 'Événements & solidarité', 'Communication interne', 'Tableaux de bord avancés', 'Support prioritaire'], 'PREMIUM': ['Tout Standard +', 'Multi-organisations', 'Analytics temps réel', 'API ouverte', 'Support dédié 24/7'], }; List get _filteredFormules => widget.formules .where((f) => _selectedPlage == null || f.plage == _selectedPlage) .toList() ..sort((a, b) => a.ordreAffichage.compareTo(b.ordreAffichage)); bool get _canProceed => _selectedPlage != null && _selectedFormule != null; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: UnionFlowColors.background, body: Column( children: [ OnboardingStepHeader( step: 1, total: 3, title: 'Choisissez votre formule', subtitle: 'Sélectionnez la taille de votre organisation\npuis le niveau d\'abonnement adapté.', ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 8, 20, 100), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Step 1a: Taille de l'organisation OnboardingSectionTitle( icon: Icons.people_alt_outlined, title: 'Taille de votre organisation', ), const SizedBox(height: 12), ...(_plages.map((p) => _PlageCard( plage: p, selected: _selectedPlage == p.code, onTap: () => setState(() { _selectedPlage = p.code; _selectedFormule = null; }), ))), if (_selectedPlage != null) ...[ const SizedBox(height: 28), OnboardingSectionTitle( icon: Icons.workspace_premium_outlined, title: 'Niveau d\'abonnement', ), const SizedBox(height: 12), ..._filteredFormules.map((f) => _FormuleCard( formule: f, color: _formuleColors[f.code] ?? UnionFlowColors.unionGreen, icon: _formuleIcons[f.code] ?? Icons.star_border_rounded, features: _formuleFeatures[f.code] ?? [], selected: _selectedFormule == f.code, onTap: () => setState(() => _selectedFormule = f.code), )), ], ], ), ), ), ], ), bottomNavigationBar: OnboardingBottomBar( enabled: _canProceed, label: 'Choisir la période', onPressed: () => context.read().add( OnboardingFormuleSelected( codeFormule: _selectedFormule!, plage: _selectedPlage!, ), ), ), ); } } // ─── Widgets locaux ────────────────────────────────────────────────────────── class _Plage { final String code, label, sublabel, description; final IconData icon; const _Plage(this.code, this.label, this.sublabel, this.icon, this.description); } class _PlageCard extends StatelessWidget { final _Plage plage; final bool selected; final VoidCallback onTap; const _PlageCard({required this.plage, required this.selected, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 200), margin: const EdgeInsets.only(bottom: 10), decoration: BoxDecoration( color: selected ? UnionFlowColors.unionGreenPale : UnionFlowColors.surface, border: Border.all( color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.border, width: selected ? 2 : 1, ), borderRadius: BorderRadius.circular(14), boxShadow: selected ? UnionFlowColors.greenGlowShadow : UnionFlowColors.softShadow, ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.unionGreenPale, borderRadius: BorderRadius.circular(10), ), child: Icon(plage.icon, color: selected ? Colors.white : UnionFlowColors.unionGreen, size: 22), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( plage.label, style: TextStyle( fontWeight: FontWeight.w700, fontSize: 15, color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.textPrimary, ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: selected ? UnionFlowColors.unionGreen.withOpacity(0.15) : UnionFlowColors.surfaceVariant, borderRadius: BorderRadius.circular(20), ), child: Text( plage.sublabel, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w500, color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.textSecondary, ), ), ), ], ), const SizedBox(height: 2), Text( plage.description, style: const TextStyle( fontSize: 12, color: UnionFlowColors.textSecondary), ), ], ), ), Icon( selected ? Icons.check_circle_rounded : Icons.radio_button_unchecked, color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.border, size: 22, ), ], ), ), ), ); } } class _FormuleCard extends StatelessWidget { final FormuleModel formule; final Color color; final IconData icon; final List features; final bool selected; final VoidCallback onTap; const _FormuleCard({ required this.formule, required this.color, required this.icon, required this.features, required this.selected, required this.onTap, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 200), margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: UnionFlowColors.surface, border: Border.all( color: selected ? color : UnionFlowColors.border, width: selected ? 2.5 : 1, ), borderRadius: BorderRadius.circular(16), boxShadow: selected ? [ BoxShadow( color: color.withOpacity(0.2), blurRadius: 20, offset: const Offset(0, 8), ) ] : UnionFlowColors.softShadow, ), child: Column( children: [ // Header Container( padding: const EdgeInsets.fromLTRB(16, 14, 16, 14), decoration: BoxDecoration( color: selected ? color : color.withOpacity(0.06), borderRadius: const BorderRadius.vertical(top: Radius.circular(14)), ), child: Row( children: [ Icon(icon, color: selected ? Colors.white : color, size: 24), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( formule.libelle, style: TextStyle( color: selected ? Colors.white : color, fontWeight: FontWeight.w800, fontSize: 16, ), ), if (formule.description != null) Text( formule.description!, style: TextStyle( color: selected ? Colors.white70 : UnionFlowColors.textSecondary, fontSize: 12, ), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatPrix(formule.prixMensuel), style: TextStyle( color: selected ? Colors.white : color, fontWeight: FontWeight.w900, fontSize: 20, ), ), Text( 'FCFA / mois', style: TextStyle( color: selected ? Colors.white70 : UnionFlowColors.textSecondary, fontSize: 11, ), ), ], ), ], ), ), // Features Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 14), child: Column( children: features .map((f) => Padding( padding: const EdgeInsets.only(bottom: 6), child: Row( children: [ Icon(Icons.check_circle_outline_rounded, size: 16, color: color), const SizedBox(width: 8), Text(f, style: const TextStyle( fontSize: 13, color: UnionFlowColors.textPrimary)), ], ), )) .toList(), ), ), ], ), ), ); } String _formatPrix(double prix) { if (prix >= 1000000) { return '${(prix / 1000000).toStringAsFixed(1)} M'; } if (prix >= 1000) { final k = (prix / 1000).toStringAsFixed(0); return '$k 000'; } return prix.toStringAsFixed(0); } }