401 lines
12 KiB
Dart
401 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../../../../core/widgets/unified_card.dart';
|
|
import '../../../../core/theme/app_colors.dart';
|
|
import '../../../../core/theme/app_text_styles.dart';
|
|
|
|
/// Widget d'affichage des statistiques de notifications
|
|
class NotificationStatsWidget extends StatelessWidget {
|
|
final int totalCount;
|
|
final int unreadCount;
|
|
final int importantCount;
|
|
|
|
const NotificationStatsWidget({
|
|
super.key,
|
|
required this.totalCount,
|
|
required this.unreadCount,
|
|
required this.importantCount,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return UnifiedCard(
|
|
variant: UnifiedCardVariant.filled,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Row(
|
|
children: [
|
|
// Statistique principale - Non lues
|
|
Expanded(
|
|
child: _buildStatItem(
|
|
icon: Icons.mark_email_unread,
|
|
label: 'Non lues',
|
|
value: unreadCount.toString(),
|
|
color: unreadCount > 0 ? AppColors.primary : AppColors.onSurface.withOpacity(0.6),
|
|
isHighlighted: unreadCount > 0,
|
|
),
|
|
),
|
|
|
|
// Séparateur
|
|
Container(
|
|
width: 1,
|
|
height: 40,
|
|
color: AppColors.outline.withOpacity(0.2),
|
|
),
|
|
|
|
// Statistique secondaire - Importantes
|
|
Expanded(
|
|
child: _buildStatItem(
|
|
icon: Icons.star,
|
|
label: 'Importantes',
|
|
value: importantCount.toString(),
|
|
color: importantCount > 0 ? AppColors.warning : AppColors.onSurface.withOpacity(0.6),
|
|
isHighlighted: importantCount > 0,
|
|
),
|
|
),
|
|
|
|
// Séparateur
|
|
Container(
|
|
width: 1,
|
|
height: 40,
|
|
color: AppColors.outline.withOpacity(0.2),
|
|
),
|
|
|
|
// Statistique tertiaire - Total
|
|
Expanded(
|
|
child: _buildStatItem(
|
|
icon: Icons.notifications,
|
|
label: 'Total',
|
|
value: totalCount.toString(),
|
|
color: AppColors.onSurface.withOpacity(0.8),
|
|
isHighlighted: false,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatItem({
|
|
required IconData icon,
|
|
required String label,
|
|
required String value,
|
|
required Color color,
|
|
required bool isHighlighted,
|
|
}) {
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// Icône avec badge si mis en évidence
|
|
Stack(
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
Container(
|
|
width: 32,
|
|
height: 32,
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: color,
|
|
size: 18,
|
|
),
|
|
),
|
|
|
|
if (isHighlighted && value != '0')
|
|
Positioned(
|
|
right: -4,
|
|
top: -4,
|
|
child: Container(
|
|
width: 12,
|
|
height: 12,
|
|
decoration: BoxDecoration(
|
|
color: color,
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: AppColors.surface,
|
|
width: 2,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
// Valeur
|
|
Text(
|
|
value,
|
|
style: AppTextStyles.titleMedium.copyWith(
|
|
color: color,
|
|
fontWeight: isHighlighted ? FontWeight.w700 : FontWeight.w600,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 2),
|
|
|
|
// Label
|
|
Text(
|
|
label,
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: AppColors.onSurface.withOpacity(0.7),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Widget d'affichage des statistiques détaillées
|
|
class DetailedNotificationStatsWidget extends StatelessWidget {
|
|
final Map<String, dynamic> stats;
|
|
|
|
const DetailedNotificationStatsWidget({
|
|
super.key,
|
|
required this.stats,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final totalNotifications = stats['total'] ?? 0;
|
|
final unreadNotifications = stats['unread'] ?? 0;
|
|
final importantNotifications = stats['important'] ?? 0;
|
|
final archivedNotifications = stats['archived'] ?? 0;
|
|
final todayNotifications = stats['today'] ?? 0;
|
|
final weekNotifications = stats['week'] ?? 0;
|
|
final engagementRate = stats['engagement_rate'] ?? 0.0;
|
|
|
|
return UnifiedCard(
|
|
variant: UnifiedCardVariant.outlined,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// En-tête
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.analytics,
|
|
color: AppColors.primary,
|
|
size: 24,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Text(
|
|
'Statistiques détaillées',
|
|
style: AppTextStyles.titleMedium.copyWith(
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// Grille de statistiques
|
|
GridView.count(
|
|
crossAxisCount: 2,
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
childAspectRatio: 2.5,
|
|
mainAxisSpacing: 16,
|
|
crossAxisSpacing: 16,
|
|
children: [
|
|
_buildDetailedStatCard(
|
|
'Total',
|
|
totalNotifications.toString(),
|
|
Icons.notifications,
|
|
AppColors.primary,
|
|
),
|
|
_buildDetailedStatCard(
|
|
'Non lues',
|
|
unreadNotifications.toString(),
|
|
Icons.mark_email_unread,
|
|
AppColors.warning,
|
|
),
|
|
_buildDetailedStatCard(
|
|
'Importantes',
|
|
importantNotifications.toString(),
|
|
Icons.star,
|
|
AppColors.error,
|
|
),
|
|
_buildDetailedStatCard(
|
|
'Archivées',
|
|
archivedNotifications.toString(),
|
|
Icons.archive,
|
|
AppColors.onSurface.withOpacity(0.6),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// Statistiques temporelles
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: _buildTimeStatCard(
|
|
'Aujourd\'hui',
|
|
todayNotifications.toString(),
|
|
Icons.today,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: _buildTimeStatCard(
|
|
'Cette semaine',
|
|
weekNotifications.toString(),
|
|
Icons.date_range,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
// Taux d'engagement
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.primaryContainer.withOpacity(0.3),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: AppColors.primary.withOpacity(0.2),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.trending_up,
|
|
color: AppColors.primary,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Taux d\'engagement',
|
|
style: AppTextStyles.labelMedium.copyWith(
|
|
color: AppColors.primary,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
'Pourcentage de notifications ouvertes',
|
|
style: AppTextStyles.bodySmall.copyWith(
|
|
color: AppColors.onSurface.withOpacity(0.7),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Text(
|
|
'${engagementRate.toStringAsFixed(1)}%',
|
|
style: AppTextStyles.titleMedium.copyWith(
|
|
color: AppColors.primary,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDetailedStatCard(
|
|
String label,
|
|
String value,
|
|
IconData icon,
|
|
Color color,
|
|
) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.05),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: color.withOpacity(0.2),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
icon,
|
|
color: color,
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
value,
|
|
style: AppTextStyles.titleSmall.copyWith(
|
|
color: color,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
Text(
|
|
label,
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: AppColors.onSurface.withOpacity(0.7),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTimeStatCard(String label, String value, IconData icon) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceVariant.withOpacity(0.5),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: AppColors.outline.withOpacity(0.2),
|
|
),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Icon(
|
|
icon,
|
|
color: AppColors.onSurfaceVariant,
|
|
size: 20,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
value,
|
|
style: AppTextStyles.titleSmall.copyWith(
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
label,
|
|
style: AppTextStyles.labelSmall.copyWith(
|
|
color: AppColors.onSurface.withOpacity(0.7),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|