import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../bloc/onboarding_bloc.dart'; import '../../data/models/formule_model.dart'; import '../../../../features/authentication/presentation/bloc/auth_bloc.dart'; import '../../../../shared/design_system/tokens/unionflow_colors.dart'; import 'onboarding_shared_widgets.dart'; /// Étape 2 — Choix de la période de facturation /// Le type d'organisation est récupéré automatiquement depuis le backend. class PeriodSelectionPage extends StatefulWidget { final String codeFormule; final String plage; final List formules; const PeriodSelectionPage({ super.key, required this.codeFormule, required this.plage, required this.formules, }); @override State createState() => _PeriodSelectionPageState(); } class _PeriodSelectionPageState extends State { String _selectedPeriode = 'MENSUEL'; static const _periodes = [ _Periode('MENSUEL', 'Mensuel', '1 mois', null, 1, 1.00), _Periode('TRIMESTRIEL', 'Trimestriel', '3 mois', '–5%', 3, 0.95), _Periode('SEMESTRIEL', 'Semestriel', '6 mois', '–10%', 6, 0.90), _Periode('ANNUEL', 'Annuel', '12 mois', '–20%', 12, 0.80), ]; FormuleModel? get _formule => widget.formules .where((f) => f.code == widget.codeFormule && f.plage == widget.plage) .firstOrNull; double _estimerPrix(String periodeCode) { final f = _formule; if (f == null) return 0; final p = _periodes.firstWhere((x) => x.code == periodeCode); return f.prixMensuel * p.coef * p.nbMois; } String get _organisationId { final authState = context.read().state; if (authState is AuthAuthenticated) { return authState.user.organizationContexts.isNotEmpty ? authState.user.organizationContexts.first.organizationId : ''; } if (authState is AuthPendingOnboarding) { return authState.organisationId ?? ''; } return ''; } @override Widget build(BuildContext context) { final prixSelected = _estimerPrix(_selectedPeriode); return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Column( children: [ OnboardingStepHeader( step: 2, total: 3, title: 'Période de facturation', subtitle: 'Choisissez votre rythme de paiement.\nPlus la période est longue, plus vous économisez.', ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 20, 20, 100), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Rappel formule sélectionnée _FormulaRecap( codeFormule: widget.codeFormule, plage: widget.plage, prixMensuel: _formule?.prixMensuel ?? 0, ), const SizedBox(height: 24), OnboardingSectionTitle( icon: Icons.calendar_month_outlined, title: 'Choisissez votre période', ), const SizedBox(height: 12), ..._periodes.map((p) => _PeriodeCard( periode: p, selected: _selectedPeriode == p.code, prixTotal: _estimerPrix(p.code), onTap: () => setState(() => _selectedPeriode = p.code), )), const SizedBox(height: 24), // Total estimé Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( gradient: UnionFlowColors.primaryGradient, borderRadius: BorderRadius.circular(16), boxShadow: UnionFlowColors.greenGlowShadow, ), child: Row( children: [ const Icon(Icons.calculate_outlined, color: Colors.white70, size: 22), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Estimation indicative', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 12, ), ), const SizedBox(height: 2), const Text( 'Le montant exact est calculé par le système.', style: TextStyle( color: Colors.white70, fontSize: 11), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatPrix(prixSelected), style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.w900, ), ), const Text( 'FCFA', style: TextStyle(color: Colors.white70, fontSize: 12), ), ], ), ], ), ), const SizedBox(height: 16), // Note info Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: UnionFlowColors.infoPale, borderRadius: BorderRadius.circular(12), border: Border.all( color: UnionFlowColors.info.withOpacity(0.2)), ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.info_outline, color: UnionFlowColors.info, size: 18), SizedBox(width: 10), Expanded( child: Text( 'Le type de votre organisation et le coefficient tarifaire exact sont déterminés lors de la création de votre compte. Le récapitulatif final vous montrera le montant précis.', style: TextStyle( fontSize: 12, color: UnionFlowColors.info, height: 1.4), ), ), ], ), ), ], ), ), ), ], ), bottomNavigationBar: OnboardingBottomBar( enabled: true, label: 'Voir le récapitulatif', onPressed: () { context.read() ..add(OnboardingPeriodeSelected( typePeriode: _selectedPeriode, typeOrganisation: '', organisationId: _organisationId, )) ..add(const OnboardingDemandeConfirmee()); }, ), ); } String _formatPrix(double prix) { if (prix >= 1000000) return '${(prix / 1000000).toStringAsFixed(1)} M'; if (prix >= 1000) { final parts = prix.toStringAsFixed(0); if (parts.length > 3) { return '${parts.substring(0, parts.length - 3)} ${parts.substring(parts.length - 3)}'; } } return prix.toStringAsFixed(0); } } class _Periode { final String code, label, duree; final String? badge; final int nbMois; final double coef; const _Periode(this.code, this.label, this.duree, this.badge, this.nbMois, this.coef); } class _FormulaRecap extends StatelessWidget { final String codeFormule; final String plage; final double prixMensuel; const _FormulaRecap({ required this.codeFormule, required this.plage, required this.prixMensuel, }); static const _plageLabels = { 'PETITE': '1–100 membres', 'MOYENNE': '101–500 membres', 'GRANDE': '501–2 000 membres', 'TRES_GRANDE': '2 000+ membres', }; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: UnionFlowColors.unionGreenPale, borderRadius: BorderRadius.circular(12), border: Border.all( color: UnionFlowColors.unionGreen.withOpacity(0.25)), ), child: Row( children: [ const Icon(Icons.check_circle_rounded, color: UnionFlowColors.unionGreen, size: 20), const SizedBox(width: 10), Expanded( child: RichText( text: TextSpan( style: const TextStyle( color: UnionFlowColors.textPrimary, fontSize: 13), children: [ TextSpan( text: 'Formule $codeFormule', style: const TextStyle(fontWeight: FontWeight.w700), ), const TextSpan(text: ' · '), TextSpan( text: _plageLabels[plage] ?? plage, style: const TextStyle( color: UnionFlowColors.textSecondary), ), ], ), ), ), Text( '${_formatPrix(prixMensuel)} FCFA/mois', style: const TextStyle( color: UnionFlowColors.unionGreen, fontWeight: FontWeight.w700, fontSize: 13, ), ), ], ), ); } String _formatPrix(double prix) { if (prix >= 1000) { final k = (prix / 1000).toStringAsFixed(0); return '$k 000'; } return prix.toStringAsFixed(0); } } class _PeriodeCard extends StatelessWidget { final _Periode periode; final bool selected; final double prixTotal; final VoidCallback onTap; const _PeriodeCard({ required this.periode, required this.selected, required this.prixTotal, 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: [ Icon( selected ? Icons.check_circle_rounded : Icons.radio_button_unchecked, color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.border, size: 22, ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( periode.label, style: TextStyle( fontWeight: FontWeight.w700, fontSize: 15, color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.textPrimary, ), ), if (periode.badge != null) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: UnionFlowColors.successPale, borderRadius: BorderRadius.circular(20), ), child: Text( periode.badge!, style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w700, color: UnionFlowColors.success, ), ), ), ], ], ), Text( periode.duree, style: const TextStyle( fontSize: 12, color: UnionFlowColors.textSecondary), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '~ ${_formatPrix(prixTotal)}', style: TextStyle( fontWeight: FontWeight.w800, fontSize: 15, color: selected ? UnionFlowColors.unionGreen : UnionFlowColors.textPrimary, ), ), const Text( 'FCFA', style: TextStyle( fontSize: 11, color: UnionFlowColors.textSecondary), ), ], ), ], ), ), ), ); } String _formatPrix(double prix) { if (prix >= 1000000) return '${(prix / 1000000).toStringAsFixed(1)} M'; if (prix >= 1000) { final s = prix.toStringAsFixed(0); if (s.length > 3) { return '${s.substring(0, s.length - 3)} ${s.substring(s.length - 3)}'; } } return prix.toStringAsFixed(0); } }