Alignement design systeme OK

This commit is contained in:
DahoudG
2025-09-20 03:56:11 +00:00
parent a1214bc116
commit 96a17eadbd
34 changed files with 11720 additions and 766 deletions

View File

@@ -1,178 +0,0 @@
/// Dashboard Page Stable - Redirecteur vers Dashboard Adaptatif
/// Redirige automatiquement vers le nouveau système de dashboard adaptatif
library dashboard_page_stable;
import 'package:flutter/material.dart';
import 'adaptive_dashboard_page.dart';
/// Page Dashboard Stable - Maintenant un redirecteur
///
/// Cette page redirige automatiquement vers le nouveau système
/// de dashboard adaptatif basé sur les rôles utilisateurs.
class DashboardPageStable extends StatefulWidget {
const DashboardPageStable({super.key});
@override
State<DashboardPageStable> createState() => _DashboardPageStableState();
}
class _DashboardPageStableState extends State<DashboardPageStable> {
final GlobalKey<RefreshIndicatorState> _refreshKey = GlobalKey<RefreshIndicatorState>();
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorTokens.surface,
appBar: AppBar(
title: Text(
'UnionFlow Dashboard',
style: TypographyTokens.headlineSmall.copyWith(
fontWeight: FontWeight.w700,
color: ColorTokens.onSurface,
),
),
backgroundColor: ColorTokens.surface,
elevation: 0,
actions: [
IconButton(
onPressed: () => _showNotifications(),
icon: const Icon(Icons.notifications_outlined),
tooltip: 'Notifications',
),
IconButton(
onPressed: () => _showSettings(),
icon: const Icon(Icons.settings_outlined),
tooltip: 'Paramètres',
),
],
),
drawer: DashboardDrawer(
onNavigate: _onNavigate,
onLogout: _onLogout,
),
body: RefreshIndicator(
key: _refreshKey,
onRefresh: _refreshData,
child: SingleChildScrollView(
padding: const EdgeInsets.all(SpacingTokens.md),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Message de bienvenue
DashboardWelcomeSection(
title: 'Bienvenue sur UnionFlow',
subtitle: 'Votre plateforme de gestion d\'union familiale',
),
const SizedBox(height: SpacingTokens.xl),
// Statistiques
DashboardStatsGrid(
onStatTap: _onStatTap,
),
const SizedBox(height: SpacingTokens.xl),
// Actions rapides
DashboardQuickActionsGrid(
onActionTap: _onActionTap,
),
const SizedBox(height: SpacingTokens.xl),
// Activité récente
DashboardRecentActivitySection(
onActivityTap: _onActivityTap,
),
const SizedBox(height: SpacingTokens.xl),
// Insights
DashboardInsightsSection(
onMetricTap: _onMetricTap,
),
],
),
),
),
);
}
// === CALLBACKS POUR LES WIDGETS MODULAIRES ===
/// Callback pour les actions sur les statistiques
void _onStatTap(String statType) {
debugPrint('Statistique tapée: $statType');
// TODO: Implémenter la navigation vers les détails
}
/// Callback pour les actions rapides
void _onActionTap(String actionType) {
debugPrint('Action rapide: $actionType');
// TODO: Implémenter les actions spécifiques
}
/// Callback pour les activités récentes
void _onActivityTap(String activityId) {
debugPrint('Activité tapée: $activityId');
// TODO: Implémenter la navigation vers les détails
}
/// Callback pour les métriques d'insights
void _onMetricTap(String metricType) {
debugPrint('Métrique tapée: $metricType');
// TODO: Implémenter la navigation vers les rapports
}
/// Callback pour la navigation du drawer
void _onNavigate(String route) {
Navigator.of(context).pop(); // Fermer le drawer
debugPrint('Navigation vers: $route');
// TODO: Implémenter la navigation
}
/// Callback pour la déconnexion
void _onLogout() {
Navigator.of(context).pop(); // Fermer le drawer
debugPrint('Déconnexion demandée');
// TODO: Implémenter la déconnexion
}
// === MÉTHODES UTILITAIRES ===
/// Actualise les données du dashboard
Future<void> _refreshData() async {
setState(() {
_isLoading = true;
});
// Simulation d'un appel API
await Future.delayed(const Duration(seconds: 1));
setState(() {
_isLoading = false;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Données actualisées'),
duration: Duration(seconds: 2),
),
);
}
}
/// Affiche les notifications
void _showNotifications() {
debugPrint('Afficher les notifications');
// TODO: Implémenter l'affichage des notifications
}
/// Affiche les paramètres
void _showSettings() {
debugPrint('Afficher les paramètres');
// TODO: Implémenter l'affichage des paramètres
}
}

View File

@@ -1,322 +1,275 @@
/// Dashboard Membre Actif - Activity Center Personnalisé
/// Interface personnalisée pour participation active
library active_member_dashboard;
import 'package:flutter/material.dart';
import '../../../../../core/design_system/tokens/tokens.dart';
import '../../widgets/widgets.dart';
/// Dashboard Activity Center pour Membre Actif
/// Dashboard simple pour Membre Actif
class ActiveMemberDashboard extends StatelessWidget {
const ActiveMemberDashboard({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorTokens.surface,
body: CustomScrollView(
slivers: [
// App Bar Membre Actif
SliverAppBar(
expandedHeight: 160,
floating: false,
pinned: true,
backgroundColor: const Color(0xFF00B894), // Vert communauté
flexibleSpace: FlexibleSpaceBar(
title: const Text(
'Activity Center',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
background: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF00B894), Color(0xFF00A085)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: const Center(
child: Icon(Icons.groups, color: Colors.white, size: 60),
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// En-tête de bienvenue
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF00B894), Color(0xFF00CEC9)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
),
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(SpacingTokens.md),
child: Column(
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Bienvenue personnalisé
_buildPersonalizedWelcome(),
const SizedBox(height: SpacingTokens.xl),
// Mes statistiques
_buildMyStats(),
const SizedBox(height: SpacingTokens.xl),
// Actions membres
_buildMemberActions(),
const SizedBox(height: SpacingTokens.xl),
// Événements à venir
_buildUpcomingEvents(),
const SizedBox(height: SpacingTokens.xl),
// Mon activité
_buildMyActivity(),
Text(
'Bonjour !',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8),
Text(
'Bienvenue sur votre espace membre',
style: TextStyle(
color: Colors.white70,
fontSize: 16,
),
),
],
),
),
),
],
),
);
}
Widget _buildPersonalizedWelcome() {
return Container(
padding: const EdgeInsets.all(SpacingTokens.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF00B894), Color(0xFF00CEC9)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(RadiusTokens.lg),
),
child: Row(
children: [
const CircleAvatar(
radius: 30,
backgroundColor: Colors.white,
child: Icon(Icons.person, color: Color(0xFF00B894), size: 30),
),
const SizedBox(width: SpacingTokens.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
const SizedBox(height: 24),
// Statistiques rapides
const Text(
'Mes Statistiques',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
childAspectRatio: 1.2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: [
Text(
'Bonjour, Marie !',
style: TypographyTokens.headlineMedium.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
),
_buildStatCard(
icon: Icons.event_available,
value: '12',
title: 'Événements',
color: const Color(0xFF00B894),
),
Text(
'Membre depuis 2 ans • Niveau Actif',
style: TypographyTokens.bodyMedium.copyWith(
color: Colors.white.withOpacity(0.9),
),
_buildStatCard(
icon: Icons.volunteer_activism,
value: '3',
title: 'Solidarité',
color: const Color(0xFF00CEC9),
),
_buildStatCard(
icon: Icons.payment,
value: 'À jour',
title: 'Cotisations',
color: const Color(0xFF0984E3),
),
_buildStatCard(
icon: Icons.star,
value: '4.8',
title: 'Engagement',
color: const Color(0xFFE17055),
),
],
),
),
],
const SizedBox(height: 24),
// Actions rapides
const Text(
'Actions Rapides',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
childAspectRatio: 1.5,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: [
_buildActionCard(
icon: Icons.event,
title: 'Créer Événement',
color: const Color(0xFF00B894),
onTap: () {},
),
_buildActionCard(
icon: Icons.volunteer_activism,
title: 'Demande Aide',
color: const Color(0xFF00CEC9),
onTap: () {},
),
_buildActionCard(
icon: Icons.account_circle,
title: 'Mon Profil',
color: const Color(0xFF0984E3),
onTap: () {},
),
_buildActionCard(
icon: Icons.message,
title: 'Contacter',
color: const Color(0xFFE17055),
onTap: () {},
),
],
),
const SizedBox(height: 24),
// Activités récentes
const Text(
'Activités Récentes',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Card(
child: Column(
children: [
_buildActivityItem(
icon: Icons.check_circle,
title: 'Participation confirmée',
subtitle: 'Assemblée Générale - Il y a 2h',
color: const Color(0xFF00B894),
),
const Divider(height: 1),
_buildActivityItem(
icon: Icons.payment,
title: 'Cotisation payée',
subtitle: 'Décembre 2024 - Il y a 1j',
color: const Color(0xFF0984E3),
),
const Divider(height: 1),
_buildActivityItem(
icon: Icons.event,
title: 'Événement créé',
subtitle: 'Sortie ski de fond - Il y a 3j',
color: const Color(0xFF00CEC9),
),
],
),
),
],
),
);
}
Widget _buildStatCard({
required IconData icon,
required String value,
required String title,
required Color color,
}) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color, size: 32),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
title,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Widget _buildMyStats() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Mes Statistiques',
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: SpacingTokens.md),
DashboardStatsGrid(
stats: [
DashboardStat(
icon: Icons.event_available,
value: '12',
title: 'Événements',
color: const Color(0xFF00B894),
onTap: () {},
),
DashboardStat(
icon: Icons.volunteer_activism,
value: '3',
title: 'Solidarité',
color: const Color(0xFF00CEC9),
onTap: () {},
),
DashboardStat(
icon: Icons.payment,
value: 'À jour',
title: 'Cotisations',
color: const Color(0xFF0984E3),
onTap: () {},
),
DashboardStat(
icon: Icons.star,
value: '4.8',
title: 'Engagement',
color: const Color(0xFFE17055),
onTap: () {},
),
],
onStatTap: (type) {},
),
],
);
}
Widget _buildMemberActions() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Actions Rapides',
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: SpacingTokens.md),
DashboardQuickActionsGrid(
actions: [
DashboardQuickAction(
icon: Icons.event,
title: 'Créer Événement',
subtitle: 'Organiser activité',
color: const Color(0xFF00B894),
onTap: () {},
),
DashboardQuickAction(
icon: Icons.volunteer_activism,
title: 'Demande Aide',
subtitle: 'Solidarité',
color: const Color(0xFF00CEC9),
onTap: () {},
),
DashboardQuickAction(
icon: Icons.account_circle,
title: 'Mon Profil',
subtitle: 'Modifier infos',
color: const Color(0xFF0984E3),
onTap: () {},
),
DashboardQuickAction(
icon: Icons.message,
title: 'Contacter',
subtitle: 'Support',
color: const Color(0xFFE17055),
onTap: () {},
),
],
onActionTap: (type) {},
),
],
);
}
Widget _buildUpcomingEvents() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'Événements à Venir',
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
),
const Spacer(),
TextButton(
onPressed: () {},
child: const Text('Voir tout'),
),
],
),
const SizedBox(height: SpacingTokens.md),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(RadiusTokens.md),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
Widget _buildActionCard({
required IconData icon,
required String title,
required Color color,
required VoidCallback onTap,
}) {
return Card(
elevation: 2,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ListTile(
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: const Color(0xFF00B894).withOpacity(0.1),
borderRadius: BorderRadius.circular(25),
),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('15', style: TextStyle(fontWeight: FontWeight.bold)),
Text('DÉC', style: TextStyle(fontSize: 10)),
],
),
Icon(icon, color: color, size: 28),
const SizedBox(height: 8),
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
title: const Text('Assemblée Générale'),
subtitle: const Text('Salle communale • 19h00'),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
),
const Divider(height: 1),
ListTile(
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: const Color(0xFF00CEC9).withOpacity(0.1),
borderRadius: BorderRadius.circular(25),
),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('22', style: TextStyle(fontWeight: FontWeight.bold)),
Text('DÉC', style: TextStyle(fontSize: 10)),
],
),
),
title: const Text('Soirée de Noël'),
subtitle: const Text('Restaurant Le Gourmet • 20h00'),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
textAlign: TextAlign.center,
),
],
),
),
],
),
);
}
Widget _buildMyActivity() {
return DashboardRecentActivitySection(
activities: [
DashboardActivity(
title: 'Participation confirmée',
subtitle: 'Assemblée Générale',
icon: Icons.check_circle,
color: const Color(0xFF00B894),
time: 'Il y a 2h',
),
DashboardActivity(
title: 'Cotisation payée',
subtitle: 'Décembre 2024',
icon: Icons.payment,
color: const Color(0xFF0984E3),
time: 'Il y a 1j',
),
DashboardActivity(
title: 'Événement créé',
subtitle: 'Sortie ski de fond',
icon: Icons.event,
color: const Color(0xFF00CEC9),
time: 'Il y a 3j',
),
],
onActivityTap: (id) {},
Widget _buildActivityItem({
required IconData icon,
required String title,
required String subtitle,
required Color color,
}) {
return ListTile(
leading: CircleAvatar(
backgroundColor: color.withOpacity(0.1),
child: Icon(icon, color: color, size: 20),
),
title: Text(
title,
style: const TextStyle(fontWeight: FontWeight.w500),
),
subtitle: Text(subtitle),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
);
}
}

View File

@@ -0,0 +1,725 @@
/// Dashboard Consultant - Interface Limitée
/// Interface spécialisée pour consultants externes
library consultant_dashboard;
import 'package:flutter/material.dart';
/// Dashboard pour Consultant Externe
class ConsultantDashboard extends StatefulWidget {
const ConsultantDashboard({super.key});
@override
State<ConsultantDashboard> createState() => _ConsultantDashboardState();
}
class _ConsultantDashboardState extends State<ConsultantDashboard> {
int _selectedIndex = 0;
final List<String> _consultantSections = [
'Mes Projets',
'Contacts',
'Profil',
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
appBar: AppBar(
title: Text(
'Consultant - ${_consultantSections[_selectedIndex]}',
style: const TextStyle(
color: Color(0xFF6C5CE7),
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
backgroundColor: Colors.white,
elevation: 2,
centerTitle: false,
actions: [
// Notifications consultant
IconButton(
icon: const Icon(Icons.notifications_outlined, color: Color(0xFF6C5CE7)),
onPressed: () => _showConsultantNotifications(),
tooltip: 'Mes notifications',
),
// Menu consultant
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert, color: Color(0xFF6C5CE7)),
onSelected: (value) {
switch (value) {
case 'profile':
_editProfile();
break;
case 'contact':
_contactSupport();
break;
case 'help':
_showHelp();
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'profile',
child: Row(
children: [
Icon(Icons.person, size: 20, color: Color(0xFF6C5CE7)),
SizedBox(width: 12),
Text('Mon Profil'),
],
),
),
const PopupMenuItem(
value: 'contact',
child: Row(
children: [
Icon(Icons.support_agent, size: 20, color: Color(0xFF6C5CE7)),
SizedBox(width: 12),
Text('Support'),
],
),
),
const PopupMenuItem(
value: 'help',
child: Row(
children: [
Icon(Icons.help, size: 20, color: Color(0xFF6C5CE7)),
SizedBox(width: 12),
Text('Aide'),
],
),
),
],
),
],
),
drawer: _buildConsultantDrawer(),
body: Stack(
children: [
_buildSelectedContent(),
// Navigation rapide consultant
Positioned(
bottom: 20,
left: 20,
right: 20,
child: _buildConsultantQuickNavigation(),
),
],
),
);
}
/// Drawer de navigation consultant
Widget _buildConsultantDrawer() {
return Drawer(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF6C5CE7),
Color(0xFF5A4FCF),
Color(0xFF4834D4),
],
),
),
child: Column(
children: [
// Header consultant
Container(
padding: const EdgeInsets.fromLTRB(20, 60, 20, 20),
child: Row(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(30),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 2,
),
),
child: const Icon(
Icons.business_center,
color: Colors.white,
size: 30,
),
),
const SizedBox(width: 16),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Sophie Martin',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
'Consultant IT',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
),
),
],
),
),
// Menu de navigation
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 8),
itemCount: _consultantSections.length,
itemBuilder: (context, index) {
final isSelected = _selectedIndex == index;
return Container(
margin: const EdgeInsets.symmetric(vertical: 2),
decoration: BoxDecoration(
color: isSelected
? Colors.white.withOpacity(0.2)
: Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
leading: Icon(
_getConsultantSectionIcon(index),
color: Colors.white,
size: 22,
),
title: Text(
_consultantSections[index],
style: TextStyle(
color: Colors.white,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
onTap: () {
setState(() => _selectedIndex = index);
Navigator.pop(context);
},
),
);
},
),
),
// Footer avec déconnexion
Container(
padding: const EdgeInsets.all(16),
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pop();
// TODO: Implémenter la déconnexion
},
icon: const Icon(Icons.logout, size: 16),
label: const Text('Déconnexion'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white.withOpacity(0.2),
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 40),
),
),
),
],
),
),
);
}
/// Icône pour chaque section consultant
IconData _getConsultantSectionIcon(int index) {
switch (index) {
case 0: return Icons.work;
case 1: return Icons.contacts;
case 2: return Icons.person;
default: return Icons.work;
}
}
/// Contenu de la section sélectionnée
Widget _buildSelectedContent() {
switch (_selectedIndex) {
case 0:
return _buildProjectsContent();
case 1:
return _buildContactsContent();
case 2:
return _buildProfileContent();
default:
return _buildProjectsContent();
}
}
/// Mes Projets - Vue des projets assignés
Widget _buildProjectsContent() {
return SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header projets
_buildProjectsHeader(),
const SizedBox(height: 20),
// Projets actifs
_buildActiveProjects(),
const SizedBox(height: 20),
// Tâches en cours
_buildCurrentTasks(),
const SizedBox(height: 20),
// Statistiques consultant
_buildConsultantStats(),
],
),
);
}
/// Placeholder pour les autres sections
Widget _buildContactsContent() {
return const Center(
child: Text(
'Contacts\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
Widget _buildProfileContent() {
return const Center(
child: Text(
'Mon Profil\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
/// Header projets
Widget _buildProjectsHeader() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: const Row(
children: [
Icon(Icons.work, color: Color(0xFF6C5CE7), size: 24),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Mes Projets Assignés',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
'3 projets actifs',
style: TextStyle(
color: Colors.grey,
fontSize: 14,
),
),
],
),
),
],
),
);
}
/// Projets actifs
Widget _buildActiveProjects() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Projets Actifs',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_buildProjectCard(
'Refonte Site Web',
'Développement frontend',
'75%',
const Color(0xFF00B894),
),
const SizedBox(height: 8),
_buildProjectCard(
'App Mobile',
'Interface utilisateur',
'45%',
const Color(0xFF0984E3),
),
const SizedBox(height: 8),
_buildProjectCard(
'API Backend',
'Architecture serveur',
'90%',
const Color(0xFFE17055),
),
],
);
}
/// Widget pour une carte de projet
Widget _buildProjectCard(String title, String description, String progress, Color color) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.folder, color: color, size: 20),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
Text(
description,
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
],
),
),
Text(
progress,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
LinearProgressIndicator(
value: double.parse(progress.replaceAll('%', '')) / 100,
backgroundColor: color.withOpacity(0.2),
valueColor: AlwaysStoppedAnimation<Color>(color),
),
],
),
);
}
/// Tâches en cours
Widget _buildCurrentTasks() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Tâches du Jour',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
_buildTaskItem('Révision code frontend', true),
const SizedBox(height: 8),
_buildTaskItem('Réunion client 15h', false),
const SizedBox(height: 8),
_buildTaskItem('Tests unitaires', false),
],
),
),
],
);
}
/// Widget pour un élément de tâche
Widget _buildTaskItem(String task, bool completed) {
return Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: completed ? const Color(0xFF6C5CE7) : Colors.transparent,
border: Border.all(color: const Color(0xFF6C5CE7), width: 2),
borderRadius: BorderRadius.circular(4),
),
child: completed
? const Icon(Icons.check, color: Colors.white, size: 14)
: null,
),
const SizedBox(width: 12),
Expanded(
child: Text(
task,
style: TextStyle(
fontSize: 14,
decoration: completed ? TextDecoration.lineThrough : null,
color: completed ? Colors.grey[600] : Colors.black,
),
),
),
],
);
}
/// Statistiques consultant
Widget _buildConsultantStats() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Mes Statistiques',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildStatCard('Projets', '3', Icons.work, const Color(0xFF6C5CE7)),
),
const SizedBox(width: 12),
Expanded(
child: _buildStatCard('Tâches', '12', Icons.task, const Color(0xFF00B894)),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildStatCard('Heures', '156h', Icons.schedule, const Color(0xFF0984E3)),
),
const SizedBox(width: 12),
Expanded(
child: _buildStatCard('Éval.', '4.8/5', Icons.star, const Color(0xFFFDAB00)),
),
],
),
],
);
}
/// Widget pour une carte de statistique
Widget _buildStatCard(String title, String value, IconData icon, Color color) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 24),
),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
title,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12,
),
),
],
),
);
}
/// Navigation rapide consultant
Widget _buildConsultantQuickNavigation() {
return Container(
height: 60,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 5),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildNavItem(Icons.work, 'Projets', 0),
_buildNavItem(Icons.contacts, 'Contacts', 1),
_buildNavItem(Icons.person, 'Profil', 2),
],
),
);
}
/// Widget pour un élément de navigation
Widget _buildNavItem(IconData icon, String label, int index) {
final isSelected = _selectedIndex == index;
return GestureDetector(
onTap: () => setState(() => _selectedIndex = index),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isSelected
? const Color(0xFF6C5CE7).withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
icon,
size: 18,
color: isSelected
? const Color(0xFF6C5CE7)
: Colors.grey[600],
),
),
const SizedBox(height: 2),
Text(
label,
style: TextStyle(
fontSize: 9,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected
? const Color(0xFF6C5CE7)
: Colors.grey[600],
),
),
],
),
),
);
}
// Méthodes d'action
void _showConsultantNotifications() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Notifications consultant - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF6C5CE7),
),
);
}
void _editProfile() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Éditer profil - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF6C5CE7),
),
);
}
void _contactSupport() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Contacter support - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF6C5CE7),
),
);
}
void _showHelp() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Aide - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF6C5CE7),
),
);
}
}

View File

@@ -0,0 +1,914 @@
/// Dashboard Gestionnaire RH - Interface Ressources Humaines
/// Outils spécialisés pour la gestion des employés et RH
library hr_manager_dashboard;
import 'package:flutter/material.dart';
/// Dashboard spécialisé pour Gestionnaire RH
///
/// Fonctionnalités RH :
/// - Gestion des employés
/// - Recrutement et onboarding
/// - Évaluations de performance
/// - Congés et absences
/// - Reporting RH
/// - Formation et développement
class HRManagerDashboard extends StatefulWidget {
const HRManagerDashboard({super.key});
@override
State<HRManagerDashboard> createState() => _HRManagerDashboardState();
}
class _HRManagerDashboardState extends State<HRManagerDashboard>
with TickerProviderStateMixin {
late TabController _tabController;
int _selectedIndex = 0;
final List<String> _hrSections = [
'Vue d\'ensemble',
'Employés',
'Recrutement',
'Évaluations',
'Congés',
'Formation',
];
@override
void initState() {
super.initState();
_tabController = TabController(length: _hrSections.length, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF8F9FA),
appBar: AppBar(
title: Text(
'RH Manager - ${_hrSections[_selectedIndex]}',
style: const TextStyle(
color: Color(0xFF00B894),
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
backgroundColor: Colors.white,
elevation: 2,
centerTitle: false,
actions: [
// Recherche employés
IconButton(
icon: const Icon(Icons.search, color: Color(0xFF00B894)),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Recherche avancée - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF00B894),
),
);
},
tooltip: 'Rechercher employés',
),
// Notifications RH
IconButton(
icon: const Icon(Icons.notifications_outlined, color: Color(0xFF00B894)),
onPressed: () => _showHRNotifications(),
tooltip: 'Notifications RH',
),
// Menu RH
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert, color: Color(0xFF00B894)),
onSelected: (value) {
switch (value) {
case 'reports':
_generateHRReports();
break;
case 'settings':
_openHRSettings();
break;
case 'export':
_exportHRData();
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'reports',
child: Row(
children: [
Icon(Icons.assessment, size: 20, color: Color(0xFF00B894)),
SizedBox(width: 12),
Text('Rapports RH'),
],
),
),
const PopupMenuItem(
value: 'settings',
child: Row(
children: [
Icon(Icons.settings, size: 20, color: Color(0xFF00B894)),
SizedBox(width: 12),
Text('Paramètres RH'),
],
),
),
const PopupMenuItem(
value: 'export',
child: Row(
children: [
Icon(Icons.download, size: 20, color: Color(0xFF00B894)),
SizedBox(width: 12),
Text('Exporter données'),
],
),
),
],
),
],
),
drawer: _buildHRDrawer(),
body: Stack(
children: [
_buildSelectedContent(),
// Navigation rapide RH
Positioned(
bottom: 20,
left: 20,
right: 20,
child: _buildHRQuickNavigation(),
),
],
),
);
}
/// Drawer de navigation RH
Widget _buildHRDrawer() {
return Drawer(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF00B894),
Color(0xFF00A085),
Color(0xFF008B75),
],
),
),
child: Column(
children: [
// Header RH
Container(
padding: const EdgeInsets.fromLTRB(20, 60, 20, 20),
child: Row(
children: [
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(30),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 2,
),
),
child: const Icon(
Icons.people,
color: Colors.white,
size: 30,
),
),
const SizedBox(width: 16),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Gestionnaire RH',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
'Ressources Humaines',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
),
),
],
),
),
// Menu de navigation
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 8),
itemCount: _hrSections.length,
itemBuilder: (context, index) {
final isSelected = _selectedIndex == index;
return Container(
margin: const EdgeInsets.symmetric(vertical: 2),
decoration: BoxDecoration(
color: isSelected
? Colors.white.withOpacity(0.2)
: Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
leading: Icon(
_getHRSectionIcon(index),
color: Colors.white,
size: 22,
),
title: Text(
_hrSections[index],
style: TextStyle(
color: Colors.white,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
onTap: () {
setState(() => _selectedIndex = index);
Navigator.pop(context);
},
),
);
},
),
),
// Footer avec déconnexion
Container(
padding: const EdgeInsets.all(16),
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pop();
// TODO: Implémenter la déconnexion
},
icon: const Icon(Icons.logout, size: 16),
label: const Text('Déconnexion'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white.withOpacity(0.2),
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 40),
),
),
),
],
),
),
);
}
/// Icône pour chaque section RH
IconData _getHRSectionIcon(int index) {
switch (index) {
case 0: return Icons.dashboard;
case 1: return Icons.people;
case 2: return Icons.person_add;
case 3: return Icons.star_rate;
case 4: return Icons.event_busy;
case 5: return Icons.school;
default: return Icons.dashboard;
}
}
/// Contenu de la section sélectionnée
Widget _buildSelectedContent() {
switch (_selectedIndex) {
case 0:
return _buildOverviewContent();
case 1:
return _buildEmployeesContent();
case 2:
return _buildRecruitmentContent();
case 3:
return _buildEvaluationsContent();
case 4:
return _buildLeavesContent();
case 5:
return _buildTrainingContent();
default:
return _buildOverviewContent();
}
}
/// Vue d'ensemble RH
Widget _buildOverviewContent() {
return SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header avec statut RH
_buildHRStatusHeader(),
const SizedBox(height: 20),
// KPIs RH
_buildHRKPIsSection(),
const SizedBox(height: 20),
// Actions rapides RH
_buildHRQuickActions(),
const SizedBox(height: 20),
// Alertes RH importantes
_buildHRAlerts(),
],
),
);
}
/// Placeholder pour les autres sections
Widget _buildEmployeesContent() {
return const Center(
child: Text(
'Gestion des Employés\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
Widget _buildRecruitmentContent() {
return const Center(
child: Text(
'Recrutement\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
Widget _buildEvaluationsContent() {
return const Center(
child: Text(
'Évaluations\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
Widget _buildLeavesContent() {
return const Center(
child: Text(
'Congés et Absences\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
Widget _buildTrainingContent() {
return const Center(
child: Text(
'Formation\n(En développement)',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
);
}
/// Header avec statut RH
Widget _buildHRStatusHeader() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF00B894), Color(0xFF00A085)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: const Color(0xFF00B894).withOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 5),
),
],
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Département RH Actif',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'Dernière sync: ${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 12,
),
),
],
),
),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
Icons.people,
color: Colors.white,
size: 28,
),
),
],
),
);
}
/// Section KPIs RH
Widget _buildHRKPIsSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Indicateurs RH',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildHRKPICard(
'Employés Actifs',
'247',
'+12 ce mois',
Icons.people,
const Color(0xFF00B894),
),
),
const SizedBox(width: 12),
Expanded(
child: _buildHRKPICard(
'Candidatures',
'34',
'+8 cette semaine',
Icons.person_add,
const Color(0xFF0984E3),
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildHRKPICard(
'En Congé',
'18',
'7.3% de l\'effectif',
Icons.event_busy,
const Color(0xFFFDAB00),
),
),
const SizedBox(width: 12),
Expanded(
child: _buildHRKPICard(
'Évaluations',
'156',
'89% complétées',
Icons.star_rate,
const Color(0xFFE17055),
),
),
],
),
],
);
}
/// Widget pour une carte KPI RH
Widget _buildHRKPICard(
String title,
String value,
String subtitle,
IconData icon,
Color color,
) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
color: color,
size: 20,
),
),
const Spacer(),
],
),
const SizedBox(height: 12),
Text(
value,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
title,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(
fontSize: 10,
color: Colors.grey[600],
),
),
],
),
);
}
/// Actions rapides RH
Widget _buildHRQuickActions() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Actions Rapides',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 1.5,
children: [
_buildHRActionCard(
'Nouveau Employé',
Icons.person_add,
const Color(0xFF00B894),
() => _addNewEmployee(),
),
_buildHRActionCard(
'Demandes Congés',
Icons.event_busy,
const Color(0xFFFDAB00),
() => _viewLeaveRequests(),
),
_buildHRActionCard(
'Évaluations',
Icons.star_rate,
const Color(0xFFE17055),
() => _viewEvaluations(),
),
_buildHRActionCard(
'Recrutement',
Icons.work,
const Color(0xFF0984E3),
() => _viewRecruitment(),
),
],
),
],
);
}
/// Widget pour une action RH
Widget _buildHRActionCard(
String title,
IconData icon,
Color color,
VoidCallback onTap,
) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withOpacity(0.2)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
icon,
color: color,
size: 24,
),
),
const SizedBox(height: 8),
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
/// Alertes RH importantes
Widget _buildHRAlerts() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Alertes Importantes',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_buildHRAlertItem(
'Évaluations en retard',
'12 évaluations annuelles à finaliser',
Icons.warning,
const Color(0xFFE17055),
),
const SizedBox(height: 8),
_buildHRAlertItem(
'Congés à approuver',
'5 demandes de congé en attente',
Icons.pending_actions,
const Color(0xFFFDAB00),
),
const SizedBox(height: 8),
_buildHRAlertItem(
'Nouveaux candidats',
'8 candidatures reçues cette semaine',
Icons.person_add,
const Color(0xFF0984E3),
),
],
);
}
/// Widget pour un élément d'alerte RH
Widget _buildHRAlertItem(
String title,
String message,
IconData icon,
Color color,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 12,
color: color,
),
),
Text(
message,
style: TextStyle(
fontSize: 11,
color: Colors.grey[700],
),
),
],
),
),
],
),
);
}
/// Navigation rapide RH
Widget _buildHRQuickNavigation() {
return Container(
height: 60,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 5),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildHRNavItem(Icons.dashboard, 'Vue', 0),
_buildHRNavItem(Icons.people, 'Employés', 1),
_buildHRNavItem(Icons.person_add, 'Recrutement', 2),
_buildHRNavItem(Icons.star_rate, 'Évaluations', 3),
_buildHRNavItem(Icons.event_busy, 'Congés', 4),
],
),
);
}
/// Widget pour un élément de navigation RH
Widget _buildHRNavItem(IconData icon, String label, int index) {
final isSelected = _selectedIndex == index;
return GestureDetector(
onTap: () => setState(() => _selectedIndex = index),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: isSelected
? const Color(0xFF00B894).withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
icon,
size: 18,
color: isSelected
? const Color(0xFF00B894)
: Colors.grey[600],
),
),
const SizedBox(height: 2),
Text(
label,
style: TextStyle(
fontSize: 9,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
color: isSelected
? const Color(0xFF00B894)
: Colors.grey[600],
),
),
],
),
),
);
}
// Méthodes d'action
void _showHRNotifications() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Notifications RH - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF00B894),
),
);
}
void _generateHRReports() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Rapports RH - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF00B894),
),
);
}
void _openHRSettings() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Paramètres RH - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF00B894),
),
);
}
void _exportHRData() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Export données RH - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF00B894),
),
);
}
void _addNewEmployee() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Ajouter employé - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF00B894),
),
);
}
void _viewLeaveRequests() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Demandes de congé - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFFFDAB00),
),
);
}
void _viewEvaluations() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Évaluations - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFFE17055),
),
);
}
void _viewRecruitment() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Recrutement - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF0984E3),
),
);
}
}

View File

@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import '../../../../../core/design_system/tokens/tokens.dart';
import '../../widgets/widgets.dart';
/// Dashboard Control Panel pour Administrateur d'Organisation
///
/// Fonctionnalités exclusives :
@@ -34,6 +35,89 @@ class _OrgAdminDashboardState extends State<OrgAdminDashboard> {
floating: false,
pinned: true,
backgroundColor: const Color(0xFF0984E3), // Bleu corporate
actions: [
// Recherche des membres
IconButton(
icon: const Icon(Icons.search, color: Colors.white),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Recherche avancée - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF0984E3),
),
);
},
tooltip: 'Rechercher des membres',
),
// Notifications organisation
IconButton(
icon: const Icon(Icons.notifications_outlined, color: Colors.white),
onPressed: () => _showOrgNotifications(),
tooltip: 'Notifications organisation',
),
// Menu d'options
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert, color: Colors.white),
onSelected: (value) {
switch (value) {
case 'settings':
_openOrgSettings();
break;
case 'reports':
_generateReports();
break;
case 'export':
_exportOrgData();
break;
case 'backup':
_backupOrgData();
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'settings',
child: Row(
children: [
Icon(Icons.settings, size: 20, color: Color(0xFF0984E3)),
SizedBox(width: 12),
Text('Paramètres Org'),
],
),
),
const PopupMenuItem(
value: 'reports',
child: Row(
children: [
Icon(Icons.assessment, size: 20, color: Color(0xFF0984E3)),
SizedBox(width: 12),
Text('Rapports'),
],
),
),
const PopupMenuItem(
value: 'export',
child: Row(
children: [
Icon(Icons.download, size: 20, color: Color(0xFF0984E3)),
SizedBox(width: 12),
Text('Exporter données'),
],
),
),
const PopupMenuItem(
value: 'backup',
child: Row(
children: [
Icon(Icons.backup, size: 20, color: Color(0xFF0984E3)),
SizedBox(width: 12),
Text('Sauvegarde'),
],
),
),
],
),
],
flexibleSpace: FlexibleSpaceBar(
title: const Text(
'Control Panel',
@@ -419,7 +503,7 @@ class _OrgAdminDashboardState extends State<OrgAdminDashboard> {
),
),
const SizedBox(height: 2),
Icon(
const Icon(
Icons.arrow_forward_ios,
size: 12,
color: ColorTokens.textSecondary,
@@ -443,25 +527,25 @@ class _OrgAdminDashboardState extends State<OrgAdminDashboard> {
),
const SizedBox(height: SpacingTokens.md),
DashboardInsightsSection(
const DashboardInsightsSection(
metrics: [
DashboardMetric(
label: 'Cotisations collectées',
value: '89%',
progress: 0.89,
color: const Color(0xFF00B894),
color: Color(0xFF00B894),
),
DashboardMetric(
label: 'Budget utilisé',
value: '67%',
progress: 0.67,
color: const Color(0xFF0984E3),
color: Color(0xFF0984E3),
),
DashboardMetric(
label: 'Objectif annuel',
value: '78%',
progress: 0.78,
color: const Color(0xFFE17055),
color: Color(0xFFE17055),
),
],
),
@@ -483,26 +567,26 @@ class _OrgAdminDashboardState extends State<OrgAdminDashboard> {
const SizedBox(height: SpacingTokens.md),
DashboardRecentActivitySection(
activities: [
activities: const [
DashboardActivity(
title: 'Nouveau membre approuvé',
subtitle: 'Sophie Laurent rejoint l\'organisation',
icon: Icons.person_add,
color: const Color(0xFF00B894),
color: Color(0xFF00B894),
time: 'Il y a 2h',
),
DashboardActivity(
title: 'Budget mis à jour',
subtitle: 'Allocation événements modifiée',
icon: Icons.account_balance_wallet,
color: const Color(0xFF0984E3),
color: Color(0xFF0984E3),
time: 'Il y a 4h',
),
DashboardActivity(
title: 'Rapport généré',
subtitle: 'Rapport mensuel d\'activité',
icon: Icons.assessment,
color: const Color(0xFF6C5CE7),
color: Color(0xFF6C5CE7),
time: 'Il y a 1j',
),
],
@@ -533,6 +617,319 @@ class _OrgAdminDashboardState extends State<OrgAdminDashboard> {
void _onActivityTap(String activityId) {
// Navigation vers les détails de l'activité
}
/// Afficher les notifications de l'organisation
void _showOrgNotifications() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.7,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Color(0xFF0984E3),
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
child: Row(
children: [
const Icon(Icons.business, color: Colors.white),
const SizedBox(width: 12),
const Text(
'Notifications Organisation',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
IconButton(
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.close, color: Colors.white),
),
],
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildOrgNotificationItem(
'Nouveau membre',
'Marie Dubois a rejoint le département Marketing',
Icons.person_add,
const Color(0xFF00B894),
'10 min',
),
_buildOrgNotificationItem(
'Budget dépassé',
'Le département IT a dépassé son budget mensuel',
Icons.warning,
const Color(0xFFE17055),
'1h',
),
_buildOrgNotificationItem(
'Rapport mensuel',
'Le rapport d\'activité de mars est disponible',
Icons.assessment,
const Color(0xFF0984E3),
'2h',
),
_buildOrgNotificationItem(
'Demande de congé',
'3 nouvelles demandes de congé en attente',
Icons.event_busy,
const Color(0xFFFDAB00),
'3h',
),
],
),
),
],
),
),
);
}
/// Widget pour un élément de notification organisation
Widget _buildOrgNotificationItem(
String title,
String message,
IconData icon,
Color color,
String time,
) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[200]!),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 20),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
const SizedBox(height: 4),
Text(
message,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12,
),
),
],
),
),
Text(
time,
style: TextStyle(
color: Colors.grey[500],
fontSize: 11,
),
),
],
),
);
}
/// Ouvrir les paramètres de l'organisation
void _openOrgSettings() {
// TODO: Naviguer vers la page des paramètres organisation
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Paramètres Organisation - Fonctionnalité à implémenter'),
backgroundColor: Color(0xFF0984E3),
),
);
}
/// Générer des rapports
void _generateReports() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Générer un rapport'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Sélectionnez le type de rapport :'),
const SizedBox(height: 16),
ListTile(
leading: const Icon(Icons.people, color: Color(0xFF0984E3)),
title: const Text('Rapport Membres'),
onTap: () {
Navigator.pop(context);
_generateMemberReport();
},
),
ListTile(
leading: const Icon(Icons.attach_money, color: Color(0xFF00B894)),
title: const Text('Rapport Financier'),
onTap: () {
Navigator.pop(context);
_generateFinancialReport();
},
),
ListTile(
leading: const Icon(Icons.analytics, color: Color(0xFFE17055)),
title: const Text('Rapport d\'Activité'),
onTap: () {
Navigator.pop(context);
_generateActivityReport();
},
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
],
),
);
}
/// Générer rapport des membres
void _generateMemberReport() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Génération du rapport membres en cours...'),
backgroundColor: Color(0xFF0984E3),
),
);
}
/// Générer rapport financier
void _generateFinancialReport() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Génération du rapport financier en cours...'),
backgroundColor: Color(0xFF00B894),
),
);
}
/// Générer rapport d'activité
void _generateActivityReport() {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Génération du rapport d\'activité en cours...'),
backgroundColor: Color(0xFFE17055),
),
);
}
/// Exporter les données de l'organisation
void _exportOrgData() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Exporter les données'),
content: const Text(
'Sélectionnez le format d\'export souhaité :',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Export CSV en cours...'),
backgroundColor: Color(0xFF00B894),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0984E3),
),
child: const Text('CSV', style: TextStyle(color: Colors.white)),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Export Excel en cours...'),
backgroundColor: Color(0xFF00B894),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0984E3),
),
child: const Text('Excel', style: TextStyle(color: Colors.white)),
),
],
),
);
}
/// Sauvegarder les données de l'organisation
void _backupOrgData() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Sauvegarde Organisation'),
content: const Text(
'Voulez-vous créer une sauvegarde complète des données de l\'organisation ?',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Sauvegarde en cours...'),
backgroundColor: Color(0xFF0984E3),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0984E3),
),
child: const Text('Confirmer', style: TextStyle(color: Colors.white)),
),
],
),
);
}
}
/// Painter pour le motif corporate de l'en-tête

View File

@@ -3,9 +3,7 @@
library dashboard_quick_action_button;
import 'package:flutter/material.dart';
import '../../../../core/design_system/tokens/color_tokens.dart';
import '../../../../core/design_system/tokens/spacing_tokens.dart';
import '../../../../core/design_system/tokens/typography_tokens.dart';
/// Modèle de données pour une action rapide
class DashboardQuickAction {

View File

@@ -0,0 +1,297 @@
import 'package:flutter/material.dart';
import '../../../../core/design_system/tokens/tokens.dart';
/// Widget pour afficher une grille d'actions rapides
class DashboardQuickActionsGrid extends StatelessWidget {
final List<Widget> children;
final int crossAxisCount;
const DashboardQuickActionsGrid({
super.key,
required this.children,
this.crossAxisCount = 2,
});
@override
Widget build(BuildContext context) {
return GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: crossAxisCount,
childAspectRatio: 1.2,
crossAxisSpacing: SpacingTokens.md,
mainAxisSpacing: SpacingTokens.md,
children: children,
);
}
}
/// Widget pour une action rapide
class DashboardQuickAction extends StatelessWidget {
final String title;
final IconData icon;
final Color? color;
final VoidCallback? onTap;
const DashboardQuickAction({
super.key,
required this.title,
required this.icon,
this.color,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(RadiusTokens.md),
child: Padding(
padding: const EdgeInsets.all(SpacingTokens.lg),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 32,
color: color ?? ColorTokens.primary,
),
const SizedBox(height: SpacingTokens.sm),
Text(
title,
style: TypographyTokens.bodyMedium,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
}
}
/// Widget pour afficher une section d'activité récente
class DashboardRecentActivitySection extends StatelessWidget {
final List<Widget> children;
final String title;
const DashboardRecentActivitySection({
super.key,
required this.children,
this.title = 'Activité Récente',
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TypographyTokens.headlineSmall,
),
const SizedBox(height: SpacingTokens.md),
...children,
],
);
}
}
/// Widget pour une activité
class DashboardActivity extends StatelessWidget {
final String title;
final String subtitle;
final IconData icon;
final Color? color;
const DashboardActivity({
super.key,
required this.title,
required this.subtitle,
required this.icon,
this.color,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: SpacingTokens.sm),
child: ListTile(
leading: CircleAvatar(
backgroundColor: color ?? ColorTokens.primary,
child: Icon(icon, color: Colors.white),
),
title: Text(title),
subtitle: Text(subtitle),
),
);
}
}
/// Widget pour une section d'insights
class DashboardInsightsSection extends StatelessWidget {
final List<Widget> children;
const DashboardInsightsSection({
super.key,
required this.children,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Insights',
style: TypographyTokens.headlineSmall,
),
const SizedBox(height: SpacingTokens.md),
...children,
],
);
}
}
/// Widget pour une statistique
class DashboardStat extends StatelessWidget {
final String title;
final String value;
final IconData icon;
final Color? color;
final VoidCallback? onTap;
const DashboardStat({
super.key,
required this.title,
required this.value,
required this.icon,
this.color,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(RadiusTokens.md),
child: Padding(
padding: const EdgeInsets.all(SpacingTokens.lg),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 32,
color: color ?? ColorTokens.primary,
),
const SizedBox(height: SpacingTokens.sm),
Text(
value,
style: TypographyTokens.headlineSmall.copyWith(
fontWeight: FontWeight.bold,
color: color ?? ColorTokens.primary,
),
),
const SizedBox(height: SpacingTokens.xs),
Text(
title,
style: TypographyTokens.bodySmall,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
}
}
/// Widget pour la grille de statistiques
class DashboardStatsGrid extends StatelessWidget {
final List<Widget> children;
final int crossAxisCount;
const DashboardStatsGrid({
super.key,
required this.children,
this.crossAxisCount = 2,
});
@override
Widget build(BuildContext context) {
return GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: crossAxisCount,
childAspectRatio: 1.2,
crossAxisSpacing: SpacingTokens.md,
mainAxisSpacing: SpacingTokens.md,
children: children,
);
}
}
/// Widget pour le drawer du dashboard
class DashboardDrawer extends StatelessWidget {
const DashboardDrawer({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: ColorTokens.primary,
),
child: Text(
'UnionFlow',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
leading: const Icon(Icons.dashboard),
title: const Text('Dashboard'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.people),
title: const Text('Membres'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.event),
title: const Text('Événements'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Paramètres'),
onTap: () {
Navigator.pop(context);
},
),
],
),
);
}
}