feat(unionflow): ajout Spec-Kit, constitution, mission mutuelles
- Config Spec-Kit pour Spec-Driven Development - CONSTITUTION.md + .specify/memory/constitution.md - Commandes Cursor /speckit.*, règles projet - Mission: associations + mutuelles d'épargne et de financement - .gitignore: versionner config spec-kit unionflow Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Dashboard simple pour Membre Actif
|
||||
class ActiveMemberDashboard extends StatelessWidget {
|
||||
const ActiveMemberDashboard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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),
|
||||
),
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
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: [
|
||||
_buildStatCard(
|
||||
icon: Icons.event_available,
|
||||
value: '12',
|
||||
title: 'Événements',
|
||||
color: const Color(0xFF00B894),
|
||||
),
|
||||
_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 _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: [
|
||||
Icon(icon, color: color, size: 28),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,767 @@
|
||||
/// Dashboard Consultant - Interface Limitée
|
||||
/// Interface spécialisée pour consultants externes
|
||||
library consultant_dashboard;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../profile/presentation/pages/profile_page_wrapper.dart';
|
||||
import '../../../../help/presentation/pages/help_support_page.dart';
|
||||
import '../../../../notifications/presentation/pages/notifications_page_wrapper.dart';
|
||||
import '../../../../authentication/presentation/bloc/auth_bloc.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();
|
||||
context.read<AuthBloc>().add(const AuthLogoutRequested());
|
||||
},
|
||||
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 SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 80),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
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: [
|
||||
const Text(
|
||||
'Mon Profil',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
onPressed: _editProfile,
|
||||
icon: const Icon(Icons.edit, size: 20),
|
||||
label: const Text('Éditer mon profil'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF6C5CE7),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NotificationsPageWrapper(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _editProfile() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfilePageWrapper(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _contactSupport() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const HelpSupportPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showHelp() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const HelpSupportPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,915 @@
|
||||
/// 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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../members/presentation/pages/members_page_wrapper.dart';
|
||||
import '../../../../notifications/presentation/pages/notifications_page_wrapper.dart';
|
||||
import '../../../../settings/presentation/pages/system_settings_page.dart';
|
||||
import '../../../../reports/presentation/pages/reports_page_wrapper.dart';
|
||||
import '../../../../authentication/presentation/bloc/auth_bloc.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: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const MembersPageWrapper(),
|
||||
),
|
||||
);
|
||||
},
|
||||
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();
|
||||
context.read<AuthBloc>().add(const AuthLogoutRequested());
|
||||
},
|
||||
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() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NotificationsPageWrapper(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _generateHRReports() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ReportsPageWrapper(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _openHRSettings() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SystemSettingsPage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _exportHRData() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ReportsPageWrapper(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/// Dashboard Modérateur - Management Hub Focalisé
|
||||
/// Outils de modération et gestion partielle
|
||||
library moderator_dashboard;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../../shared/design_system/unionflow_design_system.dart';
|
||||
import '../../widgets/dashboard_widgets.dart';
|
||||
|
||||
/// Dashboard Management Hub pour Modérateur
|
||||
class ModeratorDashboard extends StatelessWidget {
|
||||
const ModeratorDashboard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: ColorTokens.surface,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
// App Bar Modérateur
|
||||
SliverAppBar(
|
||||
expandedHeight: 160,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: const Color(0xFFE17055), // Orange focus
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: const Text(
|
||||
'Management Hub',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFFE17055), Color(0xFFD63031)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(Icons.manage_accounts, color: Colors.white, size: 60),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(SpacingTokens.md),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Métriques modération
|
||||
_buildModerationMetrics(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Actions modération
|
||||
_buildModerationActions(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Tâches en attente
|
||||
_buildPendingTasks(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Activité récente
|
||||
_buildRecentActivity(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildModerationMetrics() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Métriques de Modération',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
DashboardStatsGrid(
|
||||
stats: const [
|
||||
DashboardStat(
|
||||
icon: Icons.flag,
|
||||
value: '12',
|
||||
title: 'Signalements',
|
||||
color: Color(0xFFE17055),
|
||||
),
|
||||
DashboardStat(
|
||||
icon: Icons.pending_actions,
|
||||
value: '8',
|
||||
title: 'En Attente',
|
||||
color: Color(0xFFD63031),
|
||||
),
|
||||
DashboardStat(
|
||||
icon: Icons.check_circle,
|
||||
value: '45',
|
||||
title: 'Résolus',
|
||||
color: Color(0xFF00B894),
|
||||
),
|
||||
DashboardStat(
|
||||
icon: Icons.people,
|
||||
value: '156',
|
||||
title: 'Membres',
|
||||
color: Color(0xFF0984E3),
|
||||
),
|
||||
],
|
||||
onStatTap: (type) {},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildModerationActions() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Actions de Modération',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
DashboardQuickActionsGrid(
|
||||
children: [
|
||||
DashboardQuickAction(
|
||||
icon: Icons.gavel,
|
||||
title: 'Modérer',
|
||||
|
||||
color: const Color(0xFFE17055),
|
||||
onTap: () {},
|
||||
),
|
||||
DashboardQuickAction(
|
||||
icon: Icons.person_remove,
|
||||
title: 'Suspendre',
|
||||
|
||||
color: const Color(0xFFD63031),
|
||||
onTap: () {},
|
||||
),
|
||||
DashboardQuickAction(
|
||||
icon: Icons.message,
|
||||
title: 'Communiquer',
|
||||
|
||||
color: const Color(0xFF0984E3),
|
||||
onTap: () {},
|
||||
),
|
||||
DashboardQuickAction(
|
||||
icon: Icons.report,
|
||||
title: 'Rapport',
|
||||
|
||||
color: const Color(0xFF6C5CE7),
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPendingTasks() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Tâches en Attente',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Color(0xFFFFE0E0),
|
||||
child: Icon(Icons.flag, color: Color(0xFFD63031)),
|
||||
),
|
||||
title: Text('Contenu inapproprié signalé'),
|
||||
subtitle: Text('Commentaire sur événement'),
|
||||
trailing: Text('Urgent'),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Color(0xFFFFF3E0),
|
||||
child: Icon(Icons.person_add, color: Color(0xFFE17055)),
|
||||
),
|
||||
title: Text('Demande d\'adhésion'),
|
||||
subtitle: Text('Marie Dubois'),
|
||||
trailing: Text('2j'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecentActivity() {
|
||||
return const DashboardRecentActivitySection(
|
||||
children: [
|
||||
DashboardActivity(
|
||||
title: 'Signalement traité',
|
||||
subtitle: 'Contenu supprimé',
|
||||
icon: Icons.check_circle,
|
||||
color: Color(0xFF00B894),
|
||||
time: 'Il y a 1h',
|
||||
),
|
||||
DashboardActivity(
|
||||
title: 'Membre suspendu',
|
||||
subtitle: 'Violation des règles',
|
||||
icon: Icons.person_remove,
|
||||
color: Color(0xFFD63031),
|
||||
time: 'Il y a 3h',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
/// Export de tous les dashboards spécifiques par rôle
|
||||
/// Facilite l'importation des dashboards dans l'application
|
||||
library role_dashboards;
|
||||
|
||||
// Dashboards spécifiques par rôle
|
||||
export 'super_admin_dashboard.dart';
|
||||
export 'org_admin_dashboard.dart';
|
||||
export 'moderator_dashboard.dart';
|
||||
export 'active_member_dashboard.dart';
|
||||
export 'simple_member_dashboard.dart';
|
||||
export 'visitor_dashboard.dart';
|
||||
@@ -0,0 +1,360 @@
|
||||
/// Dashboard Membre Simple - Personal Space Minimaliste
|
||||
/// Interface simplifiée pour accès basique
|
||||
library simple_member_dashboard;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../../shared/design_system/unionflow_design_system.dart';
|
||||
import '../../widgets/dashboard_widgets.dart';
|
||||
|
||||
/// Dashboard Personal Space pour Membre Simple
|
||||
class SimpleMemberDashboard extends StatelessWidget {
|
||||
const SimpleMemberDashboard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: ColorTokens.surface,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
// App Bar Membre Simple
|
||||
SliverAppBar(
|
||||
expandedHeight: 140,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: const Color(0xFF00CEC9), // Teal simple
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: const Text(
|
||||
'Mon Espace',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFF00CEC9), Color(0xFF00B3B3)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(Icons.person, color: Colors.white, size: 50),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(SpacingTokens.md),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Profil personnel
|
||||
_buildPersonalProfile(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Mes informations
|
||||
_buildMyInfo(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Actions simples
|
||||
_buildSimpleActions(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Événements publics
|
||||
_buildPublicEvents(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Mon historique
|
||||
_buildMyHistory(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPersonalProfile() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(SpacingTokens.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.lg),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const CircleAvatar(
|
||||
radius: 35,
|
||||
backgroundColor: Color(0xFF00CEC9),
|
||||
child: Icon(Icons.person, color: Colors.white, size: 35),
|
||||
),
|
||||
const SizedBox(width: SpacingTokens.md),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Pierre Dupont',
|
||||
style: TypographyTokens.headlineMedium.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Membre depuis 6 mois',
|
||||
style: TypographyTokens.bodyMedium.copyWith(
|
||||
color: ColorTokens.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.sm),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: SpacingTokens.sm,
|
||||
vertical: SpacingTokens.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00CEC9).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.sm),
|
||||
),
|
||||
child: Text(
|
||||
'Membre Simple',
|
||||
style: TypographyTokens.bodySmall.copyWith(
|
||||
color: const Color(0xFF00CEC9),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMyInfo() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Mes Informations',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
const DashboardStatsGrid(
|
||||
stats: [
|
||||
DashboardStat(
|
||||
icon: Icons.payment,
|
||||
value: 'À jour',
|
||||
title: 'Cotisations',
|
||||
color: Color(0xFF00B894),
|
||||
),
|
||||
DashboardStat(
|
||||
icon: Icons.event,
|
||||
value: '2',
|
||||
title: 'Événements',
|
||||
color: Color(0xFF00CEC9),
|
||||
),
|
||||
DashboardStat(
|
||||
icon: Icons.account_circle,
|
||||
value: '100%',
|
||||
title: 'Profil',
|
||||
color: Color(0xFF0984E3),
|
||||
),
|
||||
DashboardStat(
|
||||
icon: Icons.notifications,
|
||||
value: '3',
|
||||
title: 'Notifications',
|
||||
color: Color(0xFFE17055),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSimpleActions() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Actions Disponibles',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
DashboardQuickActionsGrid(
|
||||
children: [
|
||||
DashboardQuickAction(
|
||||
icon: Icons.edit,
|
||||
title: 'Modifier Profil',
|
||||
color: const Color(0xFF00CEC9),
|
||||
onTap: () {},
|
||||
),
|
||||
DashboardQuickAction(
|
||||
icon: Icons.payment,
|
||||
title: 'Mes Cotisations',
|
||||
color: const Color(0xFF0984E3),
|
||||
onTap: () {},
|
||||
),
|
||||
DashboardQuickAction(
|
||||
icon: Icons.event,
|
||||
title: 'Événements',
|
||||
color: const Color(0xFF00B894),
|
||||
onTap: () {},
|
||||
),
|
||||
DashboardQuickAction(
|
||||
icon: Icons.help,
|
||||
title: 'Aide',
|
||||
color: const Color(0xFFE17055),
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPublicEvents() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Événements Disponibles',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00B894).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.event,
|
||||
color: Color(0xFF00B894),
|
||||
),
|
||||
),
|
||||
title: const Text('Assemblée Générale'),
|
||||
subtitle: const Text('15 décembre • 19h00'),
|
||||
trailing: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: SpacingTokens.sm,
|
||||
vertical: SpacingTokens.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00B894).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.sm),
|
||||
),
|
||||
child: const Text(
|
||||
'Public',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF00B894),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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 Icon(
|
||||
Icons.celebration,
|
||||
color: Color(0xFF00CEC9),
|
||||
),
|
||||
),
|
||||
title: const Text('Soirée de Noël'),
|
||||
subtitle: const Text('22 décembre • 20h00'),
|
||||
trailing: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: SpacingTokens.sm,
|
||||
vertical: SpacingTokens.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00CEC9).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.sm),
|
||||
),
|
||||
child: const Text(
|
||||
'Public',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF00CEC9),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMyHistory() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Mon Historique',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
const DashboardRecentActivitySection(
|
||||
children: [
|
||||
DashboardActivity(
|
||||
title: 'Cotisation payée',
|
||||
subtitle: 'Décembre 2024',
|
||||
icon: Icons.payment,
|
||||
color: Color(0xFF00B894),
|
||||
time: 'Il y a 1j',
|
||||
),
|
||||
DashboardActivity(
|
||||
title: 'Profil mis à jour',
|
||||
subtitle: 'Informations personnelles',
|
||||
icon: Icons.edit,
|
||||
color: Color(0xFF00CEC9),
|
||||
time: 'Il y a 1 sem',
|
||||
),
|
||||
DashboardActivity(
|
||||
title: 'Inscription événement',
|
||||
subtitle: 'Assemblée Générale',
|
||||
icon: Icons.event,
|
||||
color: Color(0xFF0984E3),
|
||||
time: 'Il y a 2 sem',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,554 @@
|
||||
/// Dashboard Visiteur - Landing Experience Accueillante
|
||||
/// Interface publique pour découvrir l'organisation
|
||||
library visitor_dashboard;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../../shared/design_system/tokens/color_tokens.dart';
|
||||
import '../../../../../shared/design_system/tokens/radius_tokens.dart';
|
||||
import '../../../../../shared/design_system/tokens/spacing_tokens.dart';
|
||||
import '../../../../../shared/design_system/tokens/typography_tokens.dart';
|
||||
|
||||
/// Dashboard Landing Experience pour Visiteur
|
||||
class VisitorDashboard extends StatelessWidget {
|
||||
const VisitorDashboard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: ColorTokens.surface,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
// App Bar Visiteur
|
||||
SliverAppBar(
|
||||
expandedHeight: 200,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: const Color(0xFF6C5CE7), // Indigo accueillant
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: const Text(
|
||||
'Découvrir UnionFlow',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFF6C5CE7), Color(0xFF5A4FCF)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Motif d'accueil
|
||||
Positioned.fill(
|
||||
child: CustomPaint(
|
||||
painter: _WelcomePatternPainter(),
|
||||
),
|
||||
),
|
||||
const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.waving_hand, color: Colors.white, size: 60),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
'Bienvenue !',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(SpacingTokens.md),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Message d'accueil
|
||||
_buildWelcomeMessage(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// À propos de l'organisation
|
||||
_buildAboutOrganization(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Événements publics
|
||||
_buildPublicEvents(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Comment rejoindre
|
||||
_buildHowToJoin(),
|
||||
const SizedBox(height: SpacingTokens.xl),
|
||||
|
||||
// Contact
|
||||
_buildContactInfo(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWelcomeMessage() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(SpacingTokens.lg),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFF6C5CE7), Color(0xFF5A4FCF)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.lg),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.info_outline, color: Colors.white, size: 30),
|
||||
const SizedBox(width: SpacingTokens.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Découvrez notre communauté',
|
||||
style: TypographyTokens.headlineMedium.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
Text(
|
||||
'Bienvenue sur UnionFlow ! Explorez notre organisation, découvrez nos événements publics et apprenez comment nous rejoindre.',
|
||||
style: TypographyTokens.bodyLarge.copyWith(
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
ElevatedButton(
|
||||
onPressed: () => _onJoinNow(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: const Color(0xFF6C5CE7),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: SpacingTokens.lg,
|
||||
vertical: SpacingTokens.md,
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Nous Rejoindre',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAboutOrganization() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'À Propos de Nous',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.all(SpacingTokens.lg),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF6C5CE7).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.business,
|
||||
color: Color(0xFF6C5CE7),
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: SpacingTokens.md),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Association des Développeurs',
|
||||
style: TypographyTokens.headlineSmall.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Communauté tech passionnée',
|
||||
style: TypographyTokens.bodyMedium.copyWith(
|
||||
color: ColorTokens.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
const Text(
|
||||
'Nous sommes une association dynamique qui rassemble les passionnés de technologie. Notre mission est de favoriser l\'apprentissage, le partage de connaissances et l\'entraide dans le domaine du développement.',
|
||||
style: TypographyTokens.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
|
||||
// Statistiques publiques
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildPublicStat('156', 'Membres'),
|
||||
_buildPublicStat('24', 'Événements/an'),
|
||||
_buildPublicStat('5', 'Ans d\'existence'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPublicStat(String value, String label) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
value,
|
||||
style: TypographyTokens.headlineMedium.copyWith(
|
||||
color: const Color(0xFF6C5CE7),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: TypographyTokens.bodySmall.copyWith(
|
||||
color: ColorTokens.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPublicEvents() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Événements Publics',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: const Text('Assemblée Générale Publique'),
|
||||
subtitle: const Text('Salle communale • 19h00 • Gratuit'),
|
||||
trailing: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: SpacingTokens.sm,
|
||||
vertical: SpacingTokens.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00B894).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.sm),
|
||||
),
|
||||
child: const Text(
|
||||
'OUVERT',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF00B894),
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
ListTile(
|
||||
leading: Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF6C5CE7).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
),
|
||||
child: const Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('20', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text('DÉC', style: TextStyle(fontSize: 10)),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: const Text('Conférence Tech Trends 2025'),
|
||||
subtitle: const Text('Amphithéâtre Université • 14h00 • Gratuit'),
|
||||
trailing: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: SpacingTokens.sm,
|
||||
vertical: SpacingTokens.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF6C5CE7).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(RadiusTokens.sm),
|
||||
),
|
||||
child: const Text(
|
||||
'OUVERT',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF6C5CE7),
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHowToJoin() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Comment Nous Rejoindre',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.all(SpacingTokens.lg),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildJoinStep('1', 'Créer un compte', 'Inscription gratuite en 2 minutes'),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
_buildJoinStep('2', 'Compléter le profil', 'Partagez vos centres d\'intérêt'),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
_buildJoinStep('3', 'Validation', 'Approbation par nos modérateurs'),
|
||||
const SizedBox(height: SpacingTokens.lg),
|
||||
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _onStartRegistration(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF6C5CE7),
|
||||
padding: const EdgeInsets.symmetric(vertical: SpacingTokens.md),
|
||||
),
|
||||
child: const Text(
|
||||
'Commencer l\'inscription',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildJoinStep(String number, String title, String description) {
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF6C5CE7),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
number,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: SpacingTokens.md),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TypographyTokens.bodyMedium.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
style: TypographyTokens.bodySmall.copyWith(
|
||||
color: ColorTokens.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactInfo() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Nous Contacter',
|
||||
style: TypographyTokens.headlineMedium.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: SpacingTokens.md),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.all(SpacingTokens.lg),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.email, color: Color(0xFF6C5CE7)),
|
||||
title: Text('Email'),
|
||||
subtitle: Text('contact@association-dev.fr'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.phone, color: Color(0xFF6C5CE7)),
|
||||
title: Text('Téléphone'),
|
||||
subtitle: Text('+33 1 23 45 67 89'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.location_on, color: Color(0xFF6C5CE7)),
|
||||
title: Text('Adresse'),
|
||||
subtitle: Text('123 Rue de la Tech, 75001 Paris'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// === CALLBACKS ===
|
||||
|
||||
void _onJoinNow() {
|
||||
// Navigation vers l'inscription
|
||||
}
|
||||
|
||||
void _onStartRegistration() {
|
||||
// Démarrer le processus d'inscription
|
||||
}
|
||||
}
|
||||
|
||||
/// Painter pour le motif d'accueil
|
||||
class _WelcomePatternPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = Colors.white.withOpacity(0.1)
|
||||
..strokeWidth = 1
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
// Dessiner des cercles concentriques
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
canvas.drawCircle(
|
||||
Offset(size.width / 2, size.height / 2),
|
||||
i * size.width / 10,
|
||||
paint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
Reference in New Issue
Block a user