fix(mobile): URL changement mdp corrigée + v3.0 — multi-org, AppAuth, sécurité prod

Auth:
- profile_repository.dart: /api/auth/change-password → /api/membres/auth/change-password

Multi-org (Phase 3):
- OrgSelectorPage, OrgSwitcherBloc, OrgSwitcherEntry
- org_context_service.dart: headers X-Active-Organisation-Id + X-Active-Role

Navigation:
- MorePage: navigation conditionnelle par typeOrganisation
- Suppression adaptive_navigation (remplacé par main_navigation_layout)

Auth AppAuth:
- keycloak_webview_auth_service: fixes AppAuth Android
- AuthBloc: gestion REAUTH_REQUIS + premierLoginComplet

Onboarding:
- Nouveaux états: payment_method_page, onboarding_shared_widgets
- SouscriptionStatusModel mis à jour StatutValidationSouscription

Android:
- build.gradle: ProGuard/R8, network_security_config
- Gradle wrapper mis à jour
This commit is contained in:
dahoud
2026-04-07 20:56:03 +00:00
parent 22f9c7e9a1
commit 70cbd1c873
63 changed files with 9316 additions and 6122 deletions

View File

@@ -2,270 +2,210 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../shared/design_system/unionflow_design_v2.dart';
import '../../../../authentication/presentation/bloc/auth_bloc.dart';
import '../../widgets/dashboard_drawer.dart';
/// Dashboard Visiteur - Design UnionFlow Version Publique
/// Dashboard affiché pour un compte authentifié sans rôle métier actif.
/// Cas typique : nouveau membre créé par un administrateur, en attente d'activation.
class VisitorDashboard extends StatelessWidget {
const VisitorDashboard({super.key});
@override
Widget build(BuildContext context) {
final authState = context.watch<AuthBloc>().state;
final email = authState is AuthAuthenticated ? authState.user.email : '';
final firstName = authState is AuthAuthenticated ? authState.user.firstName : '';
return Scaffold(
backgroundColor: UnionFlowColors.background,
appBar: _buildAppBar(),
drawer: DashboardDrawer(
onNavigate: (route) {
Navigator.of(context).pushNamed(route);
},
onLogout: () {
context.read<AuthBloc>().add(const AuthLogoutRequested());
},
),
body: AfricanPatternBackground(
child: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Message de bienvenue
AnimatedFadeIn(
delay: const Duration(milliseconds: 100),
child: _buildWelcomeCard(),
),
const SizedBox(height: 12),
appBar: _buildAppBar(context),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 24),
// Fonctionnalités UnionFlow
AnimatedSlideIn(
delay: const Duration(milliseconds: 200),
child: const Text(
'Découvrez UnionFlow',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: UnionFlowColors.textPrimary,
),
// Icône centrale
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: UnionFlowColors.gold.withOpacity(0.12),
shape: BoxShape.circle,
),
child: const Icon(
Icons.hourglass_empty_rounded,
size: 40,
color: UnionFlowColors.gold,
),
),
const SizedBox(height: 20),
// Titre
Text(
firstName.isNotEmpty ? 'Bonjour $firstName,' : 'Bienvenue,',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w800,
color: UnionFlowColors.textPrimary,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
const Text(
'Votre compte est en attente d\'activation',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: UnionFlowColors.textPrimary,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
// Email
if (email.isNotEmpty)
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: UnionFlowColors.border),
),
),
const SizedBox(height: 8),
AnimatedFadeIn(
delay: const Duration(milliseconds: 300),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: UnionStatWidget(
label: 'Organisations',
value: '500+',
icon: Icons.business_outlined,
color: UnionFlowColors.unionGreen,
),
),
const SizedBox(width: 12),
Expanded(
child: UnionStatWidget(
label: 'Utilisateurs',
value: '10K+',
icon: Icons.people_outlined,
color: UnionFlowColors.gold,
const Icon(Icons.email_outlined, size: 14, color: UnionFlowColors.textSecondary),
const SizedBox(width: 6),
Text(
email,
style: const TextStyle(
fontSize: 13,
color: UnionFlowColors.textSecondary,
),
),
],
),
),
const SizedBox(height: 12),
const SizedBox(height: 28),
AnimatedFadeIn(
delay: const Duration(milliseconds: 400),
child: Row(
children: [
Expanded(
child: UnionStatWidget(
label: 'Transactions',
value: '1M+',
icon: Icons.payment_outlined,
color: UnionFlowColors.indigo,
),
// Message explicatif
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(12),
border: const Border(
left: BorderSide(color: UnionFlowColors.gold, width: 3),
),
boxShadow: UnionFlowColors.softShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Que se passe-t-il ?',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: UnionFlowColors.textPrimary,
),
const SizedBox(width: 12),
Expanded(
child: UnionStatWidget(
label: 'Confiance',
value: '99%',
icon: Icons.verified_outlined,
color: UnionFlowColors.success,
),
),
],
),
),
const SizedBox(height: 12),
// Avantages
AnimatedSlideIn(
delay: const Duration(milliseconds: 500),
child: const Text(
'Nos Avantages',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: UnionFlowColors.textPrimary,
),
),
),
const SizedBox(height: 8),
AnimatedFadeIn(
delay: const Duration(milliseconds: 600),
child: _buildFeature(
'Gestion Simplifiée',
'Gérez vos cotisations, épargnes et crédits en un seul endroit',
Icons.dashboard_customize,
UnionFlowColors.unionGreen,
),
),
const SizedBox(height: 12),
AnimatedFadeIn(
delay: const Duration(milliseconds: 700),
child: _buildFeature(
'Sécurité Optimale',
'Vos données sont protégées avec un chiffrement de niveau bancaire',
Icons.security,
UnionFlowColors.indigo,
),
),
const SizedBox(height: 12),
AnimatedFadeIn(
delay: const Duration(milliseconds: 800),
child: _buildFeature(
'Solidarité Africaine',
'Entraide, tontines, mutuelles et coopératives à votre portée',
Icons.favorite_outline,
UnionFlowColors.terracotta,
),
),
const SizedBox(height: 12),
AnimatedFadeIn(
delay: const Duration(milliseconds: 900),
child: _buildFeature(
'Rapports Détaillés',
'Suivi en temps réel avec exports PDF, Excel et CSV',
Icons.analytics_outlined,
UnionFlowColors.gold,
),
),
const SizedBox(height: 16),
// Call to Action
AnimatedSlideIn(
delay: const Duration(milliseconds: 1000),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: UnionFlowColors.primaryGradient,
borderRadius: BorderRadius.circular(12),
const SizedBox(height: 8),
_buildStep(
'1',
'Votre compte a été créé par l\'administrateur de votre organisation.',
UnionFlowColors.unionGreen,
),
child: Column(
children: [
const Icon(
Icons.rocket_launch,
size: 24,
color: Colors.white,
),
const SizedBox(height: 8),
const Text(
'Prêt à Commencer ?',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Colors.white,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
Text(
'Rejoignez des milliers d\'organisations qui nous font confiance',
style: TextStyle(
fontSize: 11,
color: Colors.white.withOpacity(0.9),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/login');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: UnionFlowColors.unionGreen,
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: const Text(
'Créer un Compte',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w800,
),
),
),
),
const SizedBox(height: 6),
TextButton(
onPressed: () {
Navigator.of(context).pushNamed('/login');
},
child: Text(
'Déjà membre ? Se connecter',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Colors.white.withOpacity(0.9),
),
),
),
],
const SizedBox(height: 8),
_buildStep(
'2',
'Il doit être activé par un administrateur avant que vous puissiez accéder à la plateforme.',
UnionFlowColors.gold,
),
const SizedBox(height: 8),
_buildStep(
'3',
'Une fois activé, reconnectez-vous pour accéder à votre espace.',
UnionFlowColors.indigo,
),
],
),
),
const SizedBox(height: 20),
// Bouton actualiser
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () {
context.read<AuthBloc>().add(const AuthStatusChecked());
},
icon: const Icon(Icons.refresh_rounded, size: 18),
label: const Text(
'Vérifier l\'état de mon compte',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w700),
),
style: ElevatedButton.styleFrom(
backgroundColor: UnionFlowColors.unionGreen,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 0,
),
),
],
),
),
const SizedBox(height: 12),
// Bouton déconnexion
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () {
context.read<AuthBloc>().add(const AuthLogoutRequested());
},
icon: const Icon(Icons.logout_rounded, size: 18),
label: const Text(
'Se déconnecter',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
style: OutlinedButton.styleFrom(
foregroundColor: UnionFlowColors.textSecondary,
side: const BorderSide(color: UnionFlowColors.border),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
),
),
const SizedBox(height: 32),
],
),
),
);
}
PreferredSizeWidget _buildAppBar() {
PreferredSizeWidget _buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: UnionFlowColors.surface,
elevation: 0,
automaticallyImplyLeading: false,
title: Row(
children: [
Hero(
tag: 'unionflow_logo',
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
gradient: UnionFlowColors.primaryGradient,
borderRadius: BorderRadius.circular(8),
),
alignment: Alignment.center,
child: const Text(
'U',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 18,
),
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
gradient: UnionFlowColors.primaryGradient,
borderRadius: BorderRadius.circular(8),
),
alignment: Alignment.center,
child: const Text(
'U',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 18,
),
),
),
@@ -282,7 +222,7 @@ class VisitorDashboard extends StatelessWidget {
),
),
Text(
'Découverte',
'Compte en attente',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
@@ -297,120 +237,39 @@ class VisitorDashboard extends StatelessWidget {
);
}
Widget _buildWelcomeCard() {
return Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
gradient: UnionFlowColors.subtleGradient,
borderRadius: BorderRadius.circular(10),
border: const Border(
top: BorderSide(color: UnionFlowColors.unionGreen, width: 3),
),
boxShadow: UnionFlowColors.mediumShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(
gradient: UnionFlowColors.primaryGradient,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.waving_hand,
color: Colors.white,
size: 16,
),
),
const SizedBox(width: 16),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Bienvenue sur UnionFlow',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: UnionFlowColors.textPrimary,
),
),
SizedBox(height: 4),
Text(
'Votre plateforme de gestion mutualiste et associative',
style: TextStyle(
fontSize: 11,
color: UnionFlowColors.textSecondary,
),
),
],
),
),
],
Widget _buildStep(String number, String text, Color color) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 22,
height: 22,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
const SizedBox(height: 8),
Text(
'Gérez vos mutuelles, tontines, coopératives et associations en toute simplicité. UnionFlow est la solution complète pour la solidarité africaine.',
style: TextStyle(
alignment: Alignment.center,
child: Text(
number,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w800,
color: Colors.white,
),
),
),
const SizedBox(width: 10),
Expanded(
child: Text(
text,
style: const TextStyle(
fontSize: 12,
height: 1.5,
color: UnionFlowColors.textPrimary.withOpacity(0.8),
color: UnionFlowColors.textSecondary,
),
),
],
),
);
}
Widget _buildFeature(String title, String description, IconData icon, Color color) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
decoration: BoxDecoration(
color: UnionFlowColors.surface,
borderRadius: BorderRadius.circular(8),
border: Border(
left: BorderSide(color: color, width: 3),
),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 15),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: UnionFlowColors.textPrimary,
),
),
const SizedBox(height: 4),
Text(
description,
style: const TextStyle(
fontSize: 12,
color: UnionFlowColors.textSecondary,
),
),
],
),
),
],
),
],
);
}
}