- Ajout système complet de gestion des secteurs avec contours géographiques - Import des contours départementaux depuis GeoJSON - API REST pour la gestion des secteurs (/api/sectors) - Service de géolocalisation pour déterminer les secteurs - Migration base de données avec tables x_departements_contours et sectors_adresses - Interface Flutter pour visualisation et gestion des secteurs - Ajout thème sombre dans l'application - Corrections diverses et optimisations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
225 lines
8.6 KiB
Dart
Executable File
225 lines
8.6 KiB
Dart
Executable File
import 'package:flutter/material.dart';
|
|
import 'package:geosector_app/core/theme/app_theme.dart';
|
|
import 'package:geosector_app/core/services/theme_service.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
import 'package:geosector_app/core/services/current_user_service.dart';
|
|
import 'package:geosector_app/core/repositories/user_repository.dart';
|
|
import 'package:geosector_app/core/repositories/operation_repository.dart';
|
|
import 'package:geosector_app/core/repositories/passage_repository.dart';
|
|
import 'package:geosector_app/core/repositories/sector_repository.dart';
|
|
import 'package:geosector_app/core/repositories/membre_repository.dart';
|
|
import 'package:geosector_app/core/repositories/amicale_repository.dart';
|
|
import 'package:geosector_app/core/services/sync_service.dart';
|
|
import 'package:geosector_app/core/services/connectivity_service.dart';
|
|
import 'package:geosector_app/presentation/auth/splash_page.dart';
|
|
import 'package:geosector_app/presentation/auth/login_page.dart';
|
|
import 'package:geosector_app/presentation/auth/register_page.dart';
|
|
import 'package:geosector_app/presentation/admin/admin_dashboard_page.dart';
|
|
import 'package:geosector_app/presentation/user/user_dashboard_page.dart';
|
|
|
|
// Instances globales des repositories (plus besoin d'injecter ApiService)
|
|
final operationRepository = OperationRepository();
|
|
final passageRepository = PassageRepository();
|
|
final userRepository = UserRepository();
|
|
final sectorRepository = SectorRepository();
|
|
final membreRepository = MembreRepository();
|
|
final amicaleRepository = AmicaleRepository();
|
|
final syncService = SyncService(userRepository: userRepository);
|
|
final connectivityService = ConnectivityService();
|
|
final themeService = ThemeService.instance;
|
|
|
|
class GeosectorApp extends StatelessWidget {
|
|
const GeosectorApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: themeService,
|
|
builder: (context, child) {
|
|
return MaterialApp.router(
|
|
title: 'GeoSector',
|
|
theme: AppTheme.lightTheme,
|
|
darkTheme: AppTheme.darkTheme,
|
|
themeMode: themeService.themeMode,
|
|
routerConfig: _createRouter(),
|
|
debugShowCheckedModeBanner: false,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Création du routeur avec configuration pour URLs propres
|
|
GoRouter _createRouter() {
|
|
return GoRouter(
|
|
initialLocation: '/',
|
|
routes: [
|
|
GoRoute(
|
|
path: '/',
|
|
name: 'splash',
|
|
builder: (context, state) {
|
|
// Récupérer les paramètres de query pour redirection automatique
|
|
final action = state.uri.queryParameters['action'];
|
|
final type = state.uri.queryParameters['type'];
|
|
|
|
debugPrint('GoRoute: Affichage de SplashPage avec action=$action, type=$type');
|
|
return SplashPage(action: action, type: type);
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/login',
|
|
name: 'login',
|
|
builder: (context, state) {
|
|
// Récupérer le type depuis les query parameters ou extra data
|
|
final type = state.uri.queryParameters['type'] ?? (state.extra as Map<String, dynamic>?)?['type'] as String?;
|
|
|
|
debugPrint('GoRoute: Affichage de LoginPage avec type: $type');
|
|
return LoginPage(loginType: type);
|
|
},
|
|
),
|
|
// Routes spécifiques pour chaque type de login
|
|
GoRoute(
|
|
path: '/login/user',
|
|
name: 'login-user',
|
|
builder: (context, state) {
|
|
debugPrint('GoRoute: Affichage de LoginPage pour utilisateur');
|
|
return const LoginPage(loginType: 'user');
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/login/admin',
|
|
name: 'login-admin',
|
|
builder: (context, state) {
|
|
debugPrint('GoRoute: Affichage de LoginPage pour admin');
|
|
return const LoginPage(loginType: 'admin');
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/register',
|
|
name: 'register',
|
|
builder: (context, state) {
|
|
debugPrint('GoRoute: Affichage de RegisterPage');
|
|
return const RegisterPage();
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/user',
|
|
name: 'user',
|
|
builder: (context, state) {
|
|
debugPrint('GoRoute: Affichage de UserDashboardPage');
|
|
return const UserDashboardPage();
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/admin',
|
|
name: 'admin',
|
|
builder: (context, state) {
|
|
debugPrint('GoRoute: Affichage de AdminDashboardPage');
|
|
return const AdminDashboardPage();
|
|
},
|
|
),
|
|
],
|
|
redirect: (context, state) {
|
|
final currentPath = state.uri.path;
|
|
debugPrint('GoRouter.redirect: currentPath = $currentPath');
|
|
|
|
// Pour la page racine, toujours autoriser l'affichage de la splash page
|
|
if (currentPath == '/') {
|
|
debugPrint('GoRouter.redirect: Autorisation splash page');
|
|
return null;
|
|
}
|
|
|
|
// Pages publiques qui ne nécessitent pas d'authentification
|
|
final publicPaths = ['/login', '/login/user', '/login/admin', '/register'];
|
|
if (publicPaths.any((path) => currentPath.startsWith(path))) {
|
|
debugPrint('GoRouter.redirect: Page publique autorisée: $currentPath');
|
|
return null;
|
|
}
|
|
|
|
// Vérifier l'authentification pour les pages protégées
|
|
try {
|
|
// Utiliser le nouveau CurrentUserService
|
|
final userService = CurrentUserService.instance;
|
|
final isAuthenticated = userService.isLoggedIn;
|
|
final currentUser = userService.currentUser;
|
|
|
|
debugPrint('GoRouter.redirect: isAuthenticated = $isAuthenticated');
|
|
debugPrint('GoRouter.redirect: currentUser = ${currentUser?.email}');
|
|
|
|
// Si pas authentifié, rediriger vers la splash page
|
|
if (!isAuthenticated) {
|
|
debugPrint('GoRouter.redirect: Non authentifié, redirection vers /');
|
|
return '/';
|
|
}
|
|
|
|
// Vérifier les permissions pour les pages admin
|
|
if (currentPath.startsWith('/admin')) {
|
|
final userRole = userService.userRole;
|
|
final isAdmin = userService.canAccessAdmin;
|
|
|
|
debugPrint('GoRouter.redirect: userRole = $userRole, canAccessAdmin = $isAdmin');
|
|
|
|
if (!isAdmin) {
|
|
debugPrint('GoRouter.redirect: Pas admin, redirection vers /user');
|
|
return '/user';
|
|
}
|
|
}
|
|
|
|
// Si on arrive ici, l'utilisateur a les permissions nécessaires
|
|
debugPrint('GoRouter.redirect: Accès autorisé à $currentPath');
|
|
return null;
|
|
} catch (e) {
|
|
debugPrint('GoRouter.redirect: Erreur lors de la vérification auth: $e');
|
|
// En cas d'erreur, rediriger vers la splash page pour sécurité
|
|
return '/';
|
|
}
|
|
},
|
|
// Listener pour déboguer les changements de route
|
|
refreshListenable: CurrentUserService.instance, // Écouter les changements dans CurrentUserService
|
|
debugLogDiagnostics: true, // Activer les logs de débogage
|
|
errorBuilder: (context, state) {
|
|
debugPrint('GoRouter.errorBuilder: Erreur pour ${state.uri.path}');
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Erreur de navigation'),
|
|
backgroundColor: Colors.red,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
body: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24.0),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(Icons.error_outline, size: 64, color: Colors.red),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Page non trouvée',
|
|
style: Theme.of(context).textTheme.headlineSmall,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Chemin: ${state.uri.path}',
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
ElevatedButton.icon(
|
|
onPressed: () {
|
|
debugPrint('GoRouter.errorBuilder: Retour vers /');
|
|
context.go('/');
|
|
},
|
|
icon: const Icon(Icons.home),
|
|
label: const Text('Retour à l\'accueil'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|