1101 lines
32 KiB
Dart
1101 lines
32 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// Page Notifications - UnionFlow Mobile
|
|
///
|
|
/// Page complète de gestion des notifications avec historique,
|
|
/// préférences, filtres et actions sur les notifications.
|
|
class NotificationsPage extends StatefulWidget {
|
|
const NotificationsPage({super.key});
|
|
|
|
@override
|
|
State<NotificationsPage> createState() => _NotificationsPageState();
|
|
}
|
|
|
|
class _NotificationsPageState extends State<NotificationsPage>
|
|
with TickerProviderStateMixin {
|
|
late TabController _tabController;
|
|
String _selectedFilter = 'Toutes';
|
|
bool _showOnlyUnread = false;
|
|
|
|
final List<String> _filters = [
|
|
'Toutes',
|
|
'Membres',
|
|
'Événements',
|
|
'Organisations',
|
|
'Système',
|
|
];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_tabController = TabController(length: 2, vsync: this);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_tabController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8F9FA),
|
|
body: Column(
|
|
children: [
|
|
// Header harmonisé
|
|
_buildHeader(),
|
|
|
|
// Onglets
|
|
_buildTabBar(),
|
|
|
|
// Contenu des onglets
|
|
Expanded(
|
|
child: TabBarView(
|
|
controller: _tabController,
|
|
children: [
|
|
_buildNotificationsTab(),
|
|
_buildPreferencesTab(),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Header harmonisé avec le design system
|
|
Widget _buildHeader() {
|
|
return Container(
|
|
margin: const EdgeInsets.all(12),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
gradient: const LinearGradient(
|
|
colors: [Color(0xFF6C5CE7), Color(0xFF5A4FCF)],
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(0xFF6C5CE7).withOpacity(0.3),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 8),
|
|
),
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: const Icon(
|
|
Icons.notifications,
|
|
color: Colors.white,
|
|
size: 24,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Notifications',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
Text(
|
|
'Gérer vos notifications et préférences',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.white.withOpacity(0.8),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: IconButton(
|
|
onPressed: () => _markAllAsRead(),
|
|
icon: const Icon(
|
|
Icons.done_all,
|
|
color: Colors.white,
|
|
),
|
|
tooltip: 'Tout marquer comme lu',
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.2),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: IconButton(
|
|
onPressed: () => _showNotificationSettings(),
|
|
icon: const Icon(
|
|
Icons.settings,
|
|
color: Colors.white,
|
|
),
|
|
tooltip: 'Paramètres',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Barre d'onglets
|
|
Widget _buildTabBar() {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: TabBar(
|
|
controller: _tabController,
|
|
labelColor: const Color(0xFF6C5CE7),
|
|
unselectedLabelColor: Colors.grey[600],
|
|
indicatorColor: const Color(0xFF6C5CE7),
|
|
indicatorWeight: 3,
|
|
indicatorSize: TabBarIndicatorSize.tab,
|
|
labelStyle: const TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 14,
|
|
),
|
|
unselectedLabelStyle: const TextStyle(
|
|
fontWeight: FontWeight.normal,
|
|
fontSize: 14,
|
|
),
|
|
tabs: const [
|
|
Tab(
|
|
icon: Icon(Icons.inbox),
|
|
text: 'Notifications',
|
|
),
|
|
Tab(
|
|
icon: Icon(Icons.tune),
|
|
text: 'Préférences',
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Onglet des notifications
|
|
Widget _buildNotificationsTab() {
|
|
return Column(
|
|
children: [
|
|
const SizedBox(height: 16),
|
|
|
|
// Filtres et options
|
|
_buildFiltersSection(),
|
|
|
|
// Liste des notifications
|
|
Expanded(
|
|
child: _buildNotificationsList(),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// Section filtres
|
|
Widget _buildFiltersSection() {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 12),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.filter_list,
|
|
color: Colors.grey[600],
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Filtres',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.grey[800],
|
|
),
|
|
),
|
|
const Spacer(),
|
|
Switch(
|
|
value: _showOnlyUnread,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_showOnlyUnread = value;
|
|
});
|
|
},
|
|
activeColor: const Color(0xFF6C5CE7),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'Non lues uniquement',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
Wrap(
|
|
spacing: 8,
|
|
runSpacing: 8,
|
|
children: _filters.map((filter) {
|
|
final isSelected = _selectedFilter == filter;
|
|
return _buildFilterChip(filter, isSelected);
|
|
}).toList(),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Chip de filtre
|
|
Widget _buildFilterChip(String label, bool isSelected) {
|
|
return InkWell(
|
|
onTap: () {
|
|
setState(() {
|
|
_selectedFilter = label;
|
|
});
|
|
},
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: isSelected ? const Color(0xFF6C5CE7) : Colors.grey[50],
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(
|
|
color: isSelected ? const Color(0xFF6C5CE7) : Colors.grey[300]!,
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Text(
|
|
label,
|
|
style: TextStyle(
|
|
color: isSelected ? Colors.white : Colors.grey[700],
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Liste des notifications
|
|
Widget _buildNotificationsList() {
|
|
final notifications = _getFilteredNotifications();
|
|
|
|
if (notifications.isEmpty) {
|
|
return _buildEmptyState();
|
|
}
|
|
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.all(12),
|
|
itemCount: notifications.length,
|
|
itemBuilder: (context, index) {
|
|
final notification = notifications[index];
|
|
return _buildNotificationCard(notification);
|
|
},
|
|
);
|
|
}
|
|
|
|
/// État vide
|
|
Widget _buildEmptyState() {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(24),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF6C5CE7).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(50),
|
|
),
|
|
child: const Icon(
|
|
Icons.notifications_none,
|
|
size: 48,
|
|
color: Color(0xFF6C5CE7),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
const Text(
|
|
'Aucune notification',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF1F2937),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_showOnlyUnread
|
|
? 'Toutes vos notifications ont été lues'
|
|
: 'Vous n\'avez aucune notification pour le moment',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.grey[600],
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Carte de notification
|
|
Widget _buildNotificationCard(Map<String, dynamic> notification) {
|
|
final isRead = notification['isRead'] as bool;
|
|
final type = notification['type'] as String;
|
|
final color = _getNotificationColor(type);
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: isRead ? null : Border.all(
|
|
color: const Color(0xFF6C5CE7).withOpacity(0.3),
|
|
width: 2,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: InkWell(
|
|
onTap: () => _handleNotificationTap(notification),
|
|
borderRadius: BorderRadius.circular(16),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Icône et indicateur
|
|
Stack(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Icon(
|
|
_getNotificationIcon(type),
|
|
color: color,
|
|
size: 20,
|
|
),
|
|
),
|
|
if (!isRead)
|
|
Positioned(
|
|
top: 0,
|
|
right: 0,
|
|
child: Container(
|
|
width: 8,
|
|
height: 8,
|
|
decoration: const BoxDecoration(
|
|
color: Color(0xFF6C5CE7),
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(width: 12),
|
|
|
|
// Contenu
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
notification['title'],
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: isRead ? FontWeight.w500 : FontWeight.w600,
|
|
color: isRead ? Colors.grey[700] : const Color(0xFF1F2937),
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
notification['time'],
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[500],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
notification['message'],
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: Colors.grey[600],
|
|
height: 1.3,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
if (notification['actionText'] != null) ...[
|
|
const SizedBox(height: 8),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
notification['actionText'],
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: color,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
|
|
// Menu actions
|
|
PopupMenuButton<String>(
|
|
onSelected: (action) => _handleNotificationAction(notification, action),
|
|
itemBuilder: (context) => [
|
|
PopupMenuItem(
|
|
value: isRead ? 'mark_unread' : 'mark_read',
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
isRead ? Icons.mark_email_unread : Icons.mark_email_read,
|
|
size: 18,
|
|
color: Colors.grey[600],
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(isRead ? 'Marquer non lu' : 'Marquer comme lu'),
|
|
],
|
|
),
|
|
),
|
|
const PopupMenuItem(
|
|
value: 'delete',
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.delete,
|
|
size: 18,
|
|
color: Colors.red,
|
|
),
|
|
SizedBox(width: 8),
|
|
Text('Supprimer', style: TextStyle(color: Colors.red)),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
child: Icon(
|
|
Icons.more_vert,
|
|
color: Colors.grey[400],
|
|
size: 20,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Onglet préférences
|
|
Widget _buildPreferencesTab() {
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
children: [
|
|
const SizedBox(height: 16),
|
|
|
|
// Notifications push
|
|
_buildPreferenceSection(
|
|
'Notifications push',
|
|
'Recevoir des notifications sur votre appareil',
|
|
Icons.notifications_active,
|
|
[
|
|
_buildPreferenceItem(
|
|
'Activer les notifications',
|
|
'Recevoir toutes les notifications',
|
|
true,
|
|
(value) => _updatePreference('push_enabled', value),
|
|
),
|
|
_buildPreferenceItem(
|
|
'Sons et vibrations',
|
|
'Alertes sonores et vibrations',
|
|
true,
|
|
(value) => _updatePreference('sound_enabled', value),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// Types de notifications
|
|
_buildPreferenceSection(
|
|
'Types de notifications',
|
|
'Choisir les notifications à recevoir',
|
|
Icons.category,
|
|
[
|
|
_buildPreferenceItem(
|
|
'Nouveaux membres',
|
|
'Adhésions et modifications de profil',
|
|
true,
|
|
(value) => _updatePreference('members_notifications', value),
|
|
),
|
|
_buildPreferenceItem(
|
|
'Événements',
|
|
'Créations, modifications et rappels',
|
|
true,
|
|
(value) => _updatePreference('events_notifications', value),
|
|
),
|
|
_buildPreferenceItem(
|
|
'Organisations',
|
|
'Changements dans les organisations',
|
|
false,
|
|
(value) => _updatePreference('organizations_notifications', value),
|
|
),
|
|
_buildPreferenceItem(
|
|
'Système',
|
|
'Mises à jour et maintenance',
|
|
true,
|
|
(value) => _updatePreference('system_notifications', value),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// Email
|
|
_buildPreferenceSection(
|
|
'Notifications email',
|
|
'Recevoir des notifications par email',
|
|
Icons.email,
|
|
[
|
|
_buildPreferenceItem(
|
|
'Résumé quotidien',
|
|
'Récapitulatif des activités du jour',
|
|
false,
|
|
(value) => _updatePreference('daily_summary', value),
|
|
),
|
|
_buildPreferenceItem(
|
|
'Résumé hebdomadaire',
|
|
'Rapport hebdomadaire des activités',
|
|
true,
|
|
(value) => _updatePreference('weekly_summary', value),
|
|
),
|
|
_buildPreferenceItem(
|
|
'Notifications importantes',
|
|
'Alertes critiques uniquement',
|
|
true,
|
|
(value) => _updatePreference('important_emails', value),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 80),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Section de préférence
|
|
Widget _buildPreferenceSection(
|
|
String title,
|
|
String subtitle,
|
|
IconData icon,
|
|
List<Widget> items,
|
|
) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
icon,
|
|
color: Colors.grey[600],
|
|
size: 20,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.grey[800],
|
|
),
|
|
),
|
|
Text(
|
|
subtitle,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
...items,
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Élément de préférence
|
|
Widget _buildPreferenceItem(
|
|
String title,
|
|
String subtitle,
|
|
bool value,
|
|
Function(bool) onChanged,
|
|
) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF1F2937),
|
|
),
|
|
),
|
|
Text(
|
|
subtitle,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Switch(
|
|
value: value,
|
|
onChanged: onChanged,
|
|
activeColor: const Color(0xFF6C5CE7),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// ==================== MÉTHODES DE DONNÉES ====================
|
|
|
|
/// Obtenir les notifications filtrées
|
|
List<Map<String, dynamic>> _getFilteredNotifications() {
|
|
final allNotifications = [
|
|
{
|
|
'id': '1',
|
|
'type': 'Membres',
|
|
'title': 'Nouveau membre inscrit',
|
|
'message': 'Marie Dubois a rejoint l\'organisation Syndicat CGT Métallurgie',
|
|
'time': '2 min',
|
|
'isRead': false,
|
|
'actionText': 'Voir le profil',
|
|
},
|
|
{
|
|
'id': '2',
|
|
'type': 'Événements',
|
|
'title': 'Rappel d\'événement',
|
|
'message': 'L\'assemblée générale commence dans 1 heure (14h00)',
|
|
'time': '1h',
|
|
'isRead': false,
|
|
'actionText': 'Voir l\'événement',
|
|
},
|
|
{
|
|
'id': '3',
|
|
'type': 'Organisations',
|
|
'title': 'Modification d\'organisation',
|
|
'message': 'Les informations de contact de Fédération CGT ont été mises à jour',
|
|
'time': '3h',
|
|
'isRead': true,
|
|
'actionText': null,
|
|
},
|
|
{
|
|
'id': '4',
|
|
'type': 'Système',
|
|
'title': 'Mise à jour disponible',
|
|
'message': 'Une nouvelle version de UnionFlow est disponible (v2.1.0)',
|
|
'time': '1j',
|
|
'isRead': true,
|
|
'actionText': 'Mettre à jour',
|
|
},
|
|
{
|
|
'id': '5',
|
|
'type': 'Membres',
|
|
'title': 'Cotisation en retard',
|
|
'message': '5 membres ont des cotisations en retard ce mois-ci',
|
|
'time': '2j',
|
|
'isRead': false,
|
|
'actionText': 'Voir la liste',
|
|
},
|
|
{
|
|
'id': '6',
|
|
'type': 'Événements',
|
|
'title': 'Événement annulé',
|
|
'message': 'La formation "Négociation collective" du 15/12 a été annulée',
|
|
'time': '3j',
|
|
'isRead': true,
|
|
'actionText': null,
|
|
},
|
|
];
|
|
|
|
var filtered = allNotifications;
|
|
|
|
// Filtrer par type
|
|
if (_selectedFilter != 'Toutes') {
|
|
filtered = filtered.where((n) => n['type'] == _selectedFilter).toList();
|
|
}
|
|
|
|
// Filtrer par statut de lecture
|
|
if (_showOnlyUnread) {
|
|
filtered = filtered.where((n) => !(n['isRead'] as bool)).toList();
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
/// Obtenir la couleur selon le type de notification
|
|
Color _getNotificationColor(String type) {
|
|
switch (type) {
|
|
case 'Membres':
|
|
return const Color(0xFF6C5CE7);
|
|
case 'Événements':
|
|
return const Color(0xFF00B894);
|
|
case 'Organisations':
|
|
return const Color(0xFF0984E3);
|
|
case 'Système':
|
|
return const Color(0xFFE17055);
|
|
default:
|
|
return Colors.grey;
|
|
}
|
|
}
|
|
|
|
/// Obtenir l'icône selon le type de notification
|
|
IconData _getNotificationIcon(String type) {
|
|
switch (type) {
|
|
case 'Membres':
|
|
return Icons.person_add;
|
|
case 'Événements':
|
|
return Icons.event;
|
|
case 'Organisations':
|
|
return Icons.business;
|
|
case 'Système':
|
|
return Icons.system_update;
|
|
default:
|
|
return Icons.notifications;
|
|
}
|
|
}
|
|
|
|
// ==================== MÉTHODES D'ACTION ====================
|
|
|
|
/// Gérer le tap sur une notification
|
|
void _handleNotificationTap(Map<String, dynamic> notification) {
|
|
// Marquer comme lue si non lue
|
|
if (!(notification['isRead'] as bool)) {
|
|
setState(() {
|
|
notification['isRead'] = true;
|
|
});
|
|
}
|
|
|
|
// Action selon le type
|
|
final type = notification['type'] as String;
|
|
switch (type) {
|
|
case 'Membres':
|
|
_showSuccessSnackBar('Navigation vers la gestion des membres');
|
|
break;
|
|
case 'Événements':
|
|
_showSuccessSnackBar('Navigation vers les événements');
|
|
break;
|
|
case 'Organisations':
|
|
_showSuccessSnackBar('Navigation vers les organisations');
|
|
break;
|
|
case 'Système':
|
|
_showSystemNotificationDialog(notification);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Gérer les actions du menu contextuel
|
|
void _handleNotificationAction(Map<String, dynamic> notification, String action) {
|
|
switch (action) {
|
|
case 'mark_read':
|
|
setState(() {
|
|
notification['isRead'] = true;
|
|
});
|
|
_showSuccessSnackBar('Notification marquée comme lue');
|
|
break;
|
|
case 'mark_unread':
|
|
setState(() {
|
|
notification['isRead'] = false;
|
|
});
|
|
_showSuccessSnackBar('Notification marquée comme non lue');
|
|
break;
|
|
case 'delete':
|
|
_showDeleteConfirmationDialog(notification);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Marquer toutes les notifications comme lues
|
|
void _markAllAsRead() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Marquer tout comme lu'),
|
|
content: const Text(
|
|
'Êtes-vous sûr de vouloir marquer toutes les notifications comme lues ?',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Annuler'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
setState(() {
|
|
// Marquer toutes les notifications comme lues
|
|
final notifications = _getFilteredNotifications();
|
|
for (var notification in notifications) {
|
|
notification['isRead'] = true;
|
|
}
|
|
});
|
|
_showSuccessSnackBar('Toutes les notifications ont été marquées comme lues');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF6C5CE7),
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('Confirmer'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Afficher les paramètres de notification
|
|
void _showNotificationSettings() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Paramètres de notification'),
|
|
content: const Text(
|
|
'Utilisez l\'onglet "Préférences" pour configurer vos notifications '
|
|
'ou accédez aux paramètres système de votre appareil.',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Fermer'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
_tabController.animateTo(1); // Aller à l'onglet Préférences
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF6C5CE7),
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('Voir les préférences'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Dialogue de confirmation de suppression
|
|
void _showDeleteConfirmationDialog(Map<String, dynamic> notification) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Supprimer la notification'),
|
|
content: const Text(
|
|
'Êtes-vous sûr de vouloir supprimer cette notification ? '
|
|
'Cette action est irréversible.',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Annuler'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
setState(() {
|
|
// Simuler la suppression (dans une vraie app, on supprimerait de la base de données)
|
|
});
|
|
_showSuccessSnackBar('Notification supprimée');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('Supprimer'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Dialogue pour les notifications système
|
|
void _showSystemNotificationDialog(Map<String, dynamic> notification) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text(notification['title']),
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(notification['message']),
|
|
const SizedBox(height: 16),
|
|
if (notification['actionText'] != null)
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFE17055).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
'Action disponible : ${notification['actionText']}',
|
|
style: const TextStyle(
|
|
color: Color(0xFFE17055),
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Fermer'),
|
|
),
|
|
if (notification['actionText'] != null)
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
_showSuccessSnackBar('Action "${notification['actionText']}" exécutée');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFE17055),
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: Text(notification['actionText']),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Mettre à jour une préférence
|
|
void _updatePreference(String key, bool value) {
|
|
// Ici on sauvegarderait dans les préférences locales ou sur le serveur
|
|
_showSuccessSnackBar(
|
|
value
|
|
? 'Préférence activée'
|
|
: 'Préférence désactivée'
|
|
);
|
|
}
|
|
|
|
/// Afficher un message de succès
|
|
void _showSuccessSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: const Color(0xFF00B894),
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Afficher un message d'erreur
|
|
void _showErrorSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: const Color(0xFFE74C3C),
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
}
|
|
}
|