485 lines
14 KiB
Dart
485 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../../../../shared/theme/app_theme.dart';
|
|
import '../widgets/kpi_card.dart';
|
|
import '../widgets/clickable_kpi_card.dart';
|
|
import '../widgets/chart_card.dart';
|
|
import '../widgets/activity_feed.dart';
|
|
import '../widgets/quick_actions_grid.dart';
|
|
import '../widgets/navigation_cards.dart';
|
|
|
|
class EnhancedDashboard extends StatefulWidget {
|
|
final Function(int)? onNavigateToTab;
|
|
|
|
const EnhancedDashboard({
|
|
super.key,
|
|
this.onNavigateToTab,
|
|
});
|
|
|
|
@override
|
|
State<EnhancedDashboard> createState() => _EnhancedDashboardState();
|
|
}
|
|
|
|
class _EnhancedDashboardState extends State<EnhancedDashboard> {
|
|
final PageController _pageController = PageController();
|
|
int _currentPage = 0;
|
|
|
|
@override
|
|
void dispose() {
|
|
_pageController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: AppTheme.backgroundLight,
|
|
body: CustomScrollView(
|
|
slivers: [
|
|
_buildAppBar(),
|
|
SliverToBoxAdapter(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildWelcomeCard(),
|
|
const SizedBox(height: 24),
|
|
_buildKPISection(),
|
|
const SizedBox(height: 24),
|
|
_buildChartsSection(),
|
|
const SizedBox(height: 24),
|
|
NavigationCards(
|
|
onNavigateToTab: widget.onNavigateToTab,
|
|
),
|
|
const SizedBox(height: 24),
|
|
const QuickActionsGrid(),
|
|
const SizedBox(height: 24),
|
|
const ActivityFeed(),
|
|
const SizedBox(height: 24),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAppBar() {
|
|
return SliverAppBar(
|
|
expandedHeight: 120,
|
|
floating: false,
|
|
pinned: true,
|
|
backgroundColor: AppTheme.primaryColor,
|
|
flexibleSpace: FlexibleSpaceBar(
|
|
title: const Text(
|
|
'Tableau de bord',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
background: Container(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [AppTheme.primaryColor, AppTheme.primaryDark],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.notifications_outlined),
|
|
onPressed: () => _showNotifications(),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh),
|
|
onPressed: () => _refreshData(),
|
|
),
|
|
PopupMenuButton<String>(
|
|
icon: const Icon(Icons.more_vert),
|
|
onSelected: _handleMenuSelection,
|
|
itemBuilder: (context) => [
|
|
const PopupMenuItem(
|
|
value: 'settings',
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.settings),
|
|
SizedBox(width: 8),
|
|
Text('Paramètres'),
|
|
],
|
|
),
|
|
),
|
|
const PopupMenuItem(
|
|
value: 'export',
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.download),
|
|
SizedBox(width: 8),
|
|
Text('Exporter'),
|
|
],
|
|
),
|
|
),
|
|
const PopupMenuItem(
|
|
value: 'help',
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.help),
|
|
SizedBox(width: 8),
|
|
Text('Aide'),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildWelcomeCard() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(24),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [AppTheme.primaryColor, AppTheme.primaryLight],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: AppTheme.primaryColor.withOpacity(0.3),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 8),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Bonjour !',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Découvrez les dernières statistiques de votre association',
|
|
style: TextStyle(
|
|
color: Colors.white.withOpacity(0.9),
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 6,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(
|
|
Icons.trending_up,
|
|
color: Colors.white,
|
|
size: 16,
|
|
),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
'+12% ce mois',
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
width: 80,
|
|
height: 80,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(40),
|
|
),
|
|
child: const Icon(
|
|
Icons.dashboard_rounded,
|
|
color: Colors.white,
|
|
size: 40,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildKPISection() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const Text(
|
|
'Indicateurs clés',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppTheme.textPrimary,
|
|
),
|
|
),
|
|
TextButton.icon(
|
|
onPressed: () {},
|
|
icon: const Icon(Icons.analytics, size: 16),
|
|
label: const Text('Analyse détaillée'),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
SizedBox(
|
|
height: 180,
|
|
child: PageView(
|
|
controller: _pageController,
|
|
onPageChanged: (index) {
|
|
setState(() {
|
|
_currentPage = index;
|
|
});
|
|
},
|
|
children: [
|
|
_buildKPIPage1(),
|
|
_buildKPIPage2(),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildPageIndicator(0),
|
|
const SizedBox(width: 8),
|
|
_buildPageIndicator(1),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildKPIPage1() {
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: ClickableKPICard(
|
|
title: 'Membres actifs',
|
|
value: '1,247',
|
|
change: '+5.2%',
|
|
icon: Icons.people,
|
|
color: AppTheme.secondaryColor,
|
|
actionText: 'Gérer',
|
|
onTap: () => widget.onNavigateToTab?.call(1),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: ClickableKPICard(
|
|
title: 'Revenus mensuel',
|
|
value: '€45,890',
|
|
change: '+12.8%',
|
|
icon: Icons.euro,
|
|
color: AppTheme.successColor,
|
|
actionText: 'Finances',
|
|
onTap: () => _showFinancesMessage(),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildKPIPage2() {
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: ClickableKPICard(
|
|
title: 'Événements',
|
|
value: '23',
|
|
change: '+3',
|
|
icon: Icons.event,
|
|
color: AppTheme.warningColor,
|
|
actionText: 'Planifier',
|
|
onTap: () => widget.onNavigateToTab?.call(3),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: ClickableKPICard(
|
|
title: 'Taux cotisation',
|
|
value: '89.5%',
|
|
change: '+2.1%',
|
|
icon: Icons.payments,
|
|
color: AppTheme.accentColor,
|
|
actionText: 'Gérer',
|
|
onTap: () => widget.onNavigateToTab?.call(2),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildPageIndicator(int index) {
|
|
return AnimatedContainer(
|
|
duration: const Duration(milliseconds: 300),
|
|
width: _currentPage == index ? 20 : 8,
|
|
height: 8,
|
|
decoration: BoxDecoration(
|
|
color: _currentPage == index
|
|
? AppTheme.primaryColor
|
|
: AppTheme.primaryColor.withOpacity(0.3),
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildChartsSection() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Analyses et tendances',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: AppTheme.textPrimary,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
ChartCard(
|
|
title: 'Évolution des membres',
|
|
subtitle: 'Croissance sur 6 mois',
|
|
chart: const MembershipChart(),
|
|
onTap: () => widget.onNavigateToTab?.call(1),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: ChartCard(
|
|
title: 'Répartition',
|
|
subtitle: 'Par catégorie',
|
|
chart: const CategoryChart(),
|
|
onTap: () => widget.onNavigateToTab?.call(1),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: ChartCard(
|
|
title: 'Revenus',
|
|
subtitle: 'Évolution mensuelle',
|
|
chart: const RevenueChart(),
|
|
onTap: () => _showFinancesMessage(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
void _showNotifications() {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
builder: (context) => Container(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text(
|
|
'Notifications',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
ListTile(
|
|
leading: const Icon(Icons.warning, color: AppTheme.warningColor),
|
|
title: const Text('3 cotisations en retard'),
|
|
subtitle: const Text('Nécessite votre attention'),
|
|
onTap: () {},
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.event, color: AppTheme.accentColor),
|
|
title: const Text('Assemblée générale'),
|
|
subtitle: const Text('Dans 5 jours'),
|
|
onTap: () {},
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.check_circle, color: AppTheme.successColor),
|
|
title: const Text('Rapport mensuel'),
|
|
subtitle: const Text('Prêt à être envoyé'),
|
|
onTap: () {},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _refreshData() {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Données actualisées'),
|
|
backgroundColor: AppTheme.successColor,
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
}
|
|
|
|
void _handleMenuSelection(String value) {
|
|
switch (value) {
|
|
case 'settings':
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Paramètres - En développement')),
|
|
);
|
|
break;
|
|
case 'export':
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Export - En développement')),
|
|
);
|
|
break;
|
|
case 'help':
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Aide - En développement')),
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void _showFinancesMessage() {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Module Finances - Prochainement disponible'),
|
|
backgroundColor: AppTheme.successColor,
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
}
|
|
} |