Refactoring - Version OK
This commit is contained in:
@@ -0,0 +1,443 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../domain/entities/dashboard_entity.dart';
|
||||
import '../../bloc/dashboard_bloc.dart';
|
||||
import '../../../../../shared/design_system/dashboard_theme.dart';
|
||||
|
||||
/// Widget de notifications pour le dashboard
|
||||
class DashboardNotificationsWidget extends StatelessWidget {
|
||||
final int maxNotifications;
|
||||
|
||||
const DashboardNotificationsWidget({
|
||||
super.key,
|
||||
this.maxNotifications = 5,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: DashboardTheme.cardDecoration,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
BlocBuilder<DashboardBloc, DashboardState>(
|
||||
builder: (context, state) {
|
||||
if (state is DashboardLoading) {
|
||||
return _buildLoadingNotifications();
|
||||
} else if (state is DashboardLoaded || state is DashboardRefreshing) {
|
||||
final data = state is DashboardLoaded
|
||||
? state.dashboardData
|
||||
: (state as DashboardRefreshing).dashboardData;
|
||||
return _buildNotifications(data);
|
||||
} else if (state is DashboardError) {
|
||||
return _buildErrorNotifications();
|
||||
}
|
||||
return _buildEmptyNotifications();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing16),
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.royalBlue.withOpacity(0.1),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(DashboardTheme.borderRadius),
|
||||
topRight: Radius.circular(DashboardTheme.borderRadius),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing8),
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.royalBlue,
|
||||
borderRadius: BorderRadius.circular(DashboardTheme.borderRadiusSmall),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.notifications,
|
||||
color: DashboardTheme.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: DashboardTheme.spacing12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Notifications',
|
||||
style: DashboardTheme.titleMedium.copyWith(
|
||||
color: DashboardTheme.royalBlue,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocBuilder<DashboardBloc, DashboardState>(
|
||||
builder: (context, state) {
|
||||
if (state is DashboardLoaded || state is DashboardRefreshing) {
|
||||
final data = state is DashboardLoaded
|
||||
? state.dashboardData
|
||||
: (state as DashboardRefreshing).dashboardData;
|
||||
final urgentCount = _getUrgentNotificationsCount(data);
|
||||
|
||||
if (urgentCount > 0) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: DashboardTheme.spacing8,
|
||||
vertical: DashboardTheme.spacing4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.error,
|
||||
borderRadius: BorderRadius.circular(DashboardTheme.borderRadiusSmall),
|
||||
),
|
||||
child: Text(
|
||||
urgentCount.toString(),
|
||||
style: DashboardTheme.bodySmall.copyWith(
|
||||
color: DashboardTheme.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNotifications(DashboardEntity data) {
|
||||
final notifications = _generateNotifications(data);
|
||||
|
||||
if (notifications.isEmpty) {
|
||||
return _buildEmptyNotifications();
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: notifications.take(maxNotifications).map((notification) {
|
||||
return _buildNotificationItem(notification);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNotificationItem(DashboardNotification notification) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing16),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: DashboardTheme.grey200,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing8),
|
||||
decoration: BoxDecoration(
|
||||
color: notification.color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(DashboardTheme.borderRadiusSmall),
|
||||
),
|
||||
child: Icon(
|
||||
notification.icon,
|
||||
color: notification.color,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: DashboardTheme.spacing12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
notification.title,
|
||||
style: DashboardTheme.bodyMedium.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (notification.isUrgent) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: DashboardTheme.spacing6,
|
||||
vertical: DashboardTheme.spacing2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.error,
|
||||
borderRadius: BorderRadius.circular(DashboardTheme.borderRadiusSmall),
|
||||
),
|
||||
child: Text(
|
||||
'URGENT',
|
||||
style: DashboardTheme.bodySmall.copyWith(
|
||||
color: DashboardTheme.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: DashboardTheme.spacing4),
|
||||
Text(
|
||||
notification.message,
|
||||
style: DashboardTheme.bodySmall.copyWith(
|
||||
color: DashboardTheme.grey600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: DashboardTheme.spacing8),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
notification.timeAgo,
|
||||
style: DashboardTheme.bodySmall.copyWith(
|
||||
color: DashboardTheme.grey500,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
if (notification.actionLabel != null) ...[
|
||||
GestureDetector(
|
||||
onTap: notification.onAction,
|
||||
child: Text(
|
||||
notification.actionLabel!,
|
||||
style: DashboardTheme.bodySmall.copyWith(
|
||||
color: DashboardTheme.royalBlue,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadingNotifications() {
|
||||
return Column(
|
||||
children: List.generate(3, (index) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing16),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: DashboardTheme.grey200,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.grey200,
|
||||
borderRadius: BorderRadius.circular(DashboardTheme.borderRadiusSmall),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: DashboardTheme.spacing12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 16,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.grey200,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: DashboardTheme.spacing8),
|
||||
Container(
|
||||
height: 12,
|
||||
width: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: DashboardTheme.grey200,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorNotifications() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing24),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.error_outline,
|
||||
color: DashboardTheme.error,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(height: DashboardTheme.spacing8),
|
||||
Text(
|
||||
'Erreur de chargement',
|
||||
style: DashboardTheme.bodyMedium.copyWith(
|
||||
color: DashboardTheme.error,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyNotifications() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(DashboardTheme.spacing24),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.notifications_none,
|
||||
color: DashboardTheme.grey400,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(height: DashboardTheme.spacing8),
|
||||
Text(
|
||||
'Aucune notification',
|
||||
style: DashboardTheme.bodyMedium.copyWith(
|
||||
color: DashboardTheme.grey500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: DashboardTheme.spacing4),
|
||||
Text(
|
||||
'Vous êtes à jour !',
|
||||
style: DashboardTheme.bodySmall.copyWith(
|
||||
color: DashboardTheme.grey400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<DashboardNotification> _generateNotifications(DashboardEntity data) {
|
||||
List<DashboardNotification> notifications = [];
|
||||
|
||||
// Notification pour les demandes en attente
|
||||
if (data.stats.pendingRequests > 0) {
|
||||
notifications.add(DashboardNotification(
|
||||
title: 'Demandes en attente',
|
||||
message: '${data.stats.pendingRequests} demandes nécessitent votre attention',
|
||||
icon: Icons.pending_actions,
|
||||
color: DashboardTheme.warning,
|
||||
timeAgo: '2h',
|
||||
isUrgent: data.stats.pendingRequests > 20,
|
||||
actionLabel: 'Voir',
|
||||
onAction: () {},
|
||||
));
|
||||
}
|
||||
|
||||
// Notification pour les événements aujourd'hui
|
||||
if (data.todayEventsCount > 0) {
|
||||
notifications.add(DashboardNotification(
|
||||
title: 'Événements aujourd\'hui',
|
||||
message: '${data.todayEventsCount} événement(s) programmé(s) aujourd\'hui',
|
||||
icon: Icons.event_available,
|
||||
color: DashboardTheme.info,
|
||||
timeAgo: '30min',
|
||||
isUrgent: false,
|
||||
actionLabel: 'Voir',
|
||||
onAction: () {},
|
||||
));
|
||||
}
|
||||
|
||||
// Notification pour la croissance
|
||||
if (data.stats.hasGrowth) {
|
||||
notifications.add(DashboardNotification(
|
||||
title: 'Croissance positive',
|
||||
message: 'Croissance de ${data.stats.monthlyGrowth.toStringAsFixed(1)}% ce mois',
|
||||
icon: Icons.trending_up,
|
||||
color: DashboardTheme.success,
|
||||
timeAgo: '1j',
|
||||
isUrgent: false,
|
||||
actionLabel: null,
|
||||
onAction: null,
|
||||
));
|
||||
}
|
||||
|
||||
// Notification pour l'engagement faible
|
||||
if (!data.stats.isHighEngagement) {
|
||||
notifications.add(DashboardNotification(
|
||||
title: 'Engagement à améliorer',
|
||||
message: 'Taux d\'engagement: ${(data.stats.engagementRate * 100).toStringAsFixed(0)}%',
|
||||
icon: Icons.trending_down,
|
||||
color: DashboardTheme.error,
|
||||
timeAgo: '3h',
|
||||
isUrgent: data.stats.engagementRate < 0.5,
|
||||
actionLabel: 'Améliorer',
|
||||
onAction: () {},
|
||||
));
|
||||
}
|
||||
|
||||
// Notification pour les nouveaux membres
|
||||
if (data.recentActivitiesCount > 0) {
|
||||
notifications.add(DashboardNotification(
|
||||
title: 'Nouvelles activités',
|
||||
message: '${data.recentActivitiesCount} nouvelles activités aujourd\'hui',
|
||||
icon: Icons.fiber_new,
|
||||
color: DashboardTheme.tealBlue,
|
||||
timeAgo: '15min',
|
||||
isUrgent: false,
|
||||
actionLabel: 'Voir',
|
||||
onAction: () {},
|
||||
));
|
||||
}
|
||||
|
||||
return notifications;
|
||||
}
|
||||
|
||||
int _getUrgentNotificationsCount(DashboardEntity data) {
|
||||
final notifications = _generateNotifications(data);
|
||||
return notifications.where((n) => n.isUrgent).length;
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardNotification {
|
||||
final String title;
|
||||
final String message;
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
final String timeAgo;
|
||||
final bool isUrgent;
|
||||
final String? actionLabel;
|
||||
final VoidCallback? onAction;
|
||||
|
||||
const DashboardNotification({
|
||||
required this.title,
|
||||
required this.message,
|
||||
required this.icon,
|
||||
required this.color,
|
||||
required this.timeAgo,
|
||||
required this.isUrgent,
|
||||
this.actionLabel,
|
||||
this.onAction,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user