379 lines
13 KiB
Dart
379 lines
13 KiB
Dart
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
|
import 'package:flutter/material.dart';
|
|
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:geosector_app/core/repositories/user_repository.dart';
|
|
import 'package:geosector_app/core/theme/app_theme.dart';
|
|
import 'package:geosector_app/core/constants/app_keys.dart';
|
|
import 'package:geosector_app/presentation/widgets/dashboard_layout.dart';
|
|
|
|
// Import des pages utilisateur
|
|
import 'user_dashboard_home_page.dart';
|
|
import 'user_statistics_page.dart';
|
|
import 'user_history_page.dart';
|
|
import 'user_communication_page.dart';
|
|
import 'user_map_page.dart';
|
|
|
|
class UserDashboardPage extends StatefulWidget {
|
|
const UserDashboardPage({super.key});
|
|
|
|
@override
|
|
State<UserDashboardPage> createState() => _UserDashboardPageState();
|
|
}
|
|
|
|
class _UserDashboardPageState extends State<UserDashboardPage> {
|
|
int _selectedIndex = 0;
|
|
|
|
// Liste des pages à afficher
|
|
late final List<Widget> _pages;
|
|
|
|
// Référence à la boîte Hive pour les paramètres
|
|
late Box _settingsBox;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_pages = [
|
|
const UserDashboardHomePage(),
|
|
const UserStatisticsPage(),
|
|
const UserHistoryPage(),
|
|
const UserCommunicationPage(),
|
|
const UserMapPage(),
|
|
];
|
|
|
|
// Initialiser et charger les paramètres
|
|
_initSettings();
|
|
}
|
|
|
|
// Initialiser la boîte de paramètres et charger les préférences
|
|
Future<void> _initSettings() async {
|
|
try {
|
|
// Ouvrir la boîte de paramètres si elle n'est pas déjà ouverte
|
|
if (!Hive.isBoxOpen(AppKeys.settingsBoxName)) {
|
|
_settingsBox = await Hive.openBox(AppKeys.settingsBoxName);
|
|
} else {
|
|
_settingsBox = Hive.box(AppKeys.settingsBoxName);
|
|
}
|
|
|
|
// Charger l'index de page sélectionné
|
|
final savedIndex = _settingsBox.get('selectedPageIndex');
|
|
if (savedIndex != null &&
|
|
savedIndex is int &&
|
|
savedIndex >= 0 &&
|
|
savedIndex < _pages.length) {
|
|
setState(() {
|
|
_selectedIndex = savedIndex;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Erreur lors du chargement des paramètres: $e');
|
|
}
|
|
}
|
|
|
|
// Sauvegarder les paramètres utilisateur
|
|
void _saveSettings() {
|
|
try {
|
|
// Sauvegarder l'index de page sélectionné
|
|
_settingsBox.put('selectedPageIndex', _selectedIndex);
|
|
} catch (e) {
|
|
debugPrint('Erreur lors de la sauvegarde des paramètres: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Utiliser l'instance globale définie dans app.dart
|
|
final hasOperation = userRepository.getCurrentOperation() != null;
|
|
final hasSectors = userRepository.getUserSectors().isNotEmpty;
|
|
final isStandardUser = userRepository.currentUser != null &&
|
|
userRepository.currentUser!.role ==
|
|
'1'; // Rôle 1 = utilisateur standard
|
|
|
|
// Si l'utilisateur est standard et n'a pas d'opération assignée ou n'a pas de secteur, afficher un message spécial
|
|
final bool shouldShowNoOperationMessage = isStandardUser && !hasOperation;
|
|
final bool shouldShowNoSectorMessage = isStandardUser && !hasSectors;
|
|
|
|
// Définir les actions supplémentaires pour l'AppBar
|
|
List<Widget>? additionalActions;
|
|
if (shouldShowNoOperationMessage || shouldShowNoSectorMessage) {
|
|
additionalActions = [
|
|
// Bouton de déconnexion uniquement si l'utilisateur n'a pas d'opération
|
|
TextButton.icon(
|
|
icon: const Icon(Icons.logout, color: Colors.white),
|
|
label: const Text('Se déconnecter',
|
|
style: TextStyle(color: Colors.white)),
|
|
onPressed: () async {
|
|
// Utiliser directement userRepository pour la déconnexion
|
|
await userRepository.logoutWithUI(context);
|
|
// La redirection est gérée dans logoutWithUI
|
|
},
|
|
style: TextButton.styleFrom(
|
|
backgroundColor: AppTheme.accentColor,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
),
|
|
),
|
|
const SizedBox(width: 16), // Espacement à droite
|
|
];
|
|
}
|
|
|
|
return shouldShowNoOperationMessage
|
|
? _buildNoOperationMessage(context)
|
|
: (shouldShowNoSectorMessage
|
|
? _buildNoSectorMessage(context)
|
|
: DashboardLayout(
|
|
title: 'GEOSECTOR',
|
|
selectedIndex: _selectedIndex,
|
|
onDestinationSelected: (index) {
|
|
setState(() {
|
|
_selectedIndex = index;
|
|
_saveSettings(); // Sauvegarder l'index de page sélectionné
|
|
});
|
|
},
|
|
destinations: const [
|
|
NavigationDestination(
|
|
icon: Icon(Icons.dashboard_outlined),
|
|
selectedIcon: Icon(Icons.dashboard),
|
|
label: 'Tableau de bord',
|
|
),
|
|
NavigationDestination(
|
|
icon: Icon(Icons.bar_chart_outlined),
|
|
selectedIcon: Icon(Icons.bar_chart),
|
|
label: 'Stats',
|
|
),
|
|
NavigationDestination(
|
|
icon: Icon(Icons.history_outlined),
|
|
selectedIcon: Icon(Icons.history),
|
|
label: 'Historique',
|
|
),
|
|
NavigationDestination(
|
|
icon: Icon(Icons.chat_outlined),
|
|
selectedIcon: Icon(Icons.chat),
|
|
label: 'Messages',
|
|
),
|
|
NavigationDestination(
|
|
icon: Icon(Icons.map_outlined),
|
|
selectedIcon: Icon(Icons.map),
|
|
label: 'Carte',
|
|
),
|
|
],
|
|
additionalActions: additionalActions,
|
|
onNewPassagePressed: () => _showPassageForm(context),
|
|
body: _pages[_selectedIndex],
|
|
));
|
|
}
|
|
|
|
// Message pour les utilisateurs sans opération assignée
|
|
Widget _buildNoOperationMessage(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
|
|
return Center(
|
|
child: Container(
|
|
padding: const EdgeInsets.all(24),
|
|
constraints: const BoxConstraints(maxWidth: 500),
|
|
decoration: BoxDecoration(
|
|
color: theme.colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: theme.shadowColor.withOpacity(0.1),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
Icons.warning_amber_rounded,
|
|
size: 80,
|
|
color: theme.colorScheme.error,
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Aucune opération assignée',
|
|
style: theme.textTheme.headlineSmall?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Vous n\'avez pas encore été affecté à une opération. Veuillez contacter votre administrateur pour obtenir un accès.',
|
|
style: theme.textTheme.bodyLarge?.copyWith(
|
|
color: theme.colorScheme.onSurface.withOpacity(0.7),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Message pour les utilisateurs sans secteur assigné
|
|
Widget _buildNoSectorMessage(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
|
|
return Center(
|
|
child: Container(
|
|
padding: const EdgeInsets.all(24),
|
|
constraints: const BoxConstraints(maxWidth: 500),
|
|
decoration: BoxDecoration(
|
|
color: theme.colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: theme.shadowColor.withOpacity(0.1),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
Icons.map_outlined,
|
|
size: 80,
|
|
color: theme.colorScheme.error,
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Aucun secteur assigné',
|
|
style: theme.textTheme.headlineSmall?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Vous n\'êtes affecté sur aucun secteur. Contactez votre administrateur pour qu\'il vous en affecte au moins un.',
|
|
style: theme.textTheme.bodyLarge?.copyWith(
|
|
color: theme.colorScheme.onSurface.withOpacity(0.7),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Affiche le formulaire de passage
|
|
void _showPassageForm(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text(
|
|
'Nouveau passage',
|
|
style: TextStyle(
|
|
color: theme.colorScheme.primary,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
content: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
TextField(
|
|
decoration: InputDecoration(
|
|
labelText: 'Adresse',
|
|
prefixIcon: const Icon(Icons.location_on),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
DropdownButtonFormField<int>(
|
|
decoration: InputDecoration(
|
|
labelText: 'Type de passage',
|
|
prefixIcon: const Icon(Icons.category),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
items: const [
|
|
DropdownMenuItem(
|
|
value: 1,
|
|
child: Text('Effectué'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: 2,
|
|
child: Text('À finaliser'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: 3,
|
|
child: Text('Refusé'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: 4,
|
|
child: Text('Don'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: 5,
|
|
child: Text('Lot'),
|
|
),
|
|
DropdownMenuItem(
|
|
value: 6,
|
|
child: Text('Maison vide'),
|
|
),
|
|
],
|
|
onChanged: (value) {},
|
|
),
|
|
const SizedBox(height: 16),
|
|
TextField(
|
|
decoration: InputDecoration(
|
|
labelText: 'Commentaire',
|
|
prefixIcon: const Icon(Icons.comment),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
maxLines: 3,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
child: Text(
|
|
'Annuler',
|
|
style: TextStyle(
|
|
color: theme.colorScheme.error,
|
|
),
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
// Enregistrer le passage
|
|
Navigator.of(context).pop();
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: const Text('Passage enregistré avec succès'),
|
|
backgroundColor: theme.colorScheme.primary,
|
|
),
|
|
);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: theme.colorScheme.primary,
|
|
foregroundColor: theme.colorScheme.onPrimary,
|
|
),
|
|
child: const Text('Enregistrer'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|