Amélioration de la splash_page et du login

This commit is contained in:
d6soft
2025-06-04 16:51:40 +02:00
parent 8c9e9a21c4
commit 41f1db1169
228 changed files with 366459 additions and 6183 deletions

View File

@@ -1,12 +1,9 @@
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/constants/app_keys.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/presentation/widgets/help_dialog.dart';
import 'package:geosector_app/presentation/widgets/profile_dialog.dart';
/// Widget qui fournit une navigation responsive pour l'application.
/// Affiche une sidebar en mode desktop et une bottomBar en mode mobile.
@@ -125,7 +122,6 @@ class _ResponsiveNavigationState extends State<ResponsiveNavigation> {
appBar: widget.showAppBar
? AppBar(
title: Text(widget.title),
actions: _buildAppBarActions(context),
)
: null,
body: isDesktop ? _buildDesktopLayout() : _buildMobileLayout(),
@@ -157,38 +153,6 @@ class _ResponsiveNavigationState extends State<ResponsiveNavigation> {
);
}
/// Construction des actions de l'AppBar
List<Widget> _buildAppBarActions(BuildContext context) {
List<Widget> actions = [];
// Ajouter les actions supplémentaires si elles existent
if (widget.additionalActions != null &&
widget.additionalActions!.isNotEmpty) {
actions.addAll(widget.additionalActions!);
} else if (widget.showNewPassageButton && widget.selectedIndex == 0) {
// Ajouter le bouton "Nouveau passage" en haut à droite pour la page d'accueil
actions.add(
TextButton.icon(
icon: const Icon(Icons.add_location_alt, color: Colors.white),
label: const Text('Nouveau passage',
style: TextStyle(color: Colors.white)),
onPressed: widget.onNewPassagePressed ??
() {
// Fonction par défaut si onNewPassagePressed n'est pas fourni
_showPassageForm(context);
},
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.secondary,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
);
actions.add(const SizedBox(width: 16)); // Espacement à droite
}
return actions;
}
/// Construction de la barre de navigation inférieure pour mobile
Widget _buildBottomNavigationBar() {
final theme = Theme.of(context);
@@ -350,103 +314,15 @@ class _ResponsiveNavigationState extends State<ResponsiveNavigation> {
const Spacer(),
const Divider(),
// Éléments du bas de la sidebar
// Éléments du bas de la sidebar (widgets personnalisés)
if (widget.sidebarBottomItems != null && !_isSidebarMinimized)
...widget.sidebarBottomItems!,
// Éléments par défaut du bas de la sidebar
if (!_isSidebarMinimized)
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'Paramètres',
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
_SettingsItem(
icon: Icons.person,
title: 'Mon compte',
subtitle: null,
isSidebarMinimized: _isSidebarMinimized,
onTap: () {
// Afficher la boîte de dialogue de profil avec l'utilisateur actuel
// Utiliser l'instance globale définie dans app.dart
final user = userRepository.currentUser;
if (user != null) {
// Passer l'objet utilisateur complet
ProfileDialog.show(context, user);
} else {
// Afficher un message d'erreur si l'utilisateur n'est pas trouvé
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Erreur: Utilisateur non trouvé'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
},
),
// Option "Amicale & membres" - uniquement pour les administrateurs avec le rôle 2 et en version web
if (widget.isAdmin &&
userRepository.currentUser?.role == 2 &&
MediaQuery.of(context).size.width > 900)
_SettingsItem(
icon: Icons.people,
title: 'Amicale & membres',
isSidebarMinimized: _isSidebarMinimized,
onTap: () {
// Navigation vers le tableau de bord admin avec sélection de l'onglet "Amicale et membres"
context.go('/admin');
// Sélectionner l'onglet "Amicale et membres" (index 5)
// Nous devons sauvegarder cet index dans les paramètres pour que le tableau de bord
// puisse le récupérer et sélectionner le bon onglet
final settingsBox = Hive.box(AppKeys.settingsBoxName);
settingsBox.put('adminSelectedPageIndex', 5);
// Notifier l'utilisateur que la page est en cours de chargement
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content:
Text('Chargement de la page Amicale & membres...'),
duration: Duration(seconds: 2),
),
);
// Attendre un court instant pour permettre à la navigation de se terminer
Future.delayed(const Duration(milliseconds: 300), () {
// Forcer la sélection de l'onglet Amicale & membres
if (widget.isAdmin && widget.selectedIndex != 5) {
widget.onDestinationSelected(5);
}
});
},
),
// Option "Opérations" - uniquement pour les administrateurs et en version web
// Options administrateur - uniquement pour les administrateurs et en version web
if (widget.isAdmin && MediaQuery.of(context).size.width > 900)
_SettingsItem(
icon: Icons.calendar_today,
title: 'Opérations',
isSidebarMinimized: _isSidebarMinimized,
onTap: () {
// Navigation vers le tableau de bord admin
context.go('/admin');
...[],
// Note: Pas de page spécifique pour le moment, juste un placeholder
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Fonctionnalité à venir'),
duration: Duration(seconds: 2),
),
);
},
),
const SizedBox(height: 16),
// Option "Aide" - toujours visible en bas
_SettingsItem(
icon: Icons.help_outline,
title: 'Aide',
@@ -456,6 +332,8 @@ class _ResponsiveNavigationState extends State<ResponsiveNavigation> {
HelpDialog.show(context, widget.title);
},
),
const SizedBox(height: 16),
],
),
),
@@ -538,119 +416,6 @@ class _ResponsiveNavigationState extends State<ResponsiveNavigation> {
);
}
}
/// 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'),
),
],
),
);
}
}
/// Widget pour les éléments de paramètres