On release/v3.1.4: Sauvegarde temporaire pour changement de branche

This commit is contained in:
2025-08-21 17:51:22 +02:00
parent 6c8853e553
commit 41a4505b4b
1697 changed files with 167987 additions and 231472 deletions

View File

@@ -60,6 +60,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
bool _chkStripe = false;
bool _chkMdpManuel = false;
bool _chkUsernameManuel = false;
bool _chkUserDeletePass = false;
// Pour l'upload du logo
final ImagePicker _picker = ImagePicker();
@@ -93,6 +94,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
_chkStripe = amicale?.chkStripe ?? false;
_chkMdpManuel = amicale?.chkMdpManuel ?? false;
_chkUsernameManuel = amicale?.chkUsernameManuel ?? false;
_chkUserDeletePass = amicale?.chkUserDeletePass ?? false;
// Note : Le logo sera chargé dynamiquement depuis l'API
}
@@ -152,6 +154,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
'chk_stripe': amicale.chkStripe ? 1 : 0,
'chk_mdp_manuel': amicale.chkMdpManuel ? 1 : 0,
'chk_username_manuel': amicale.chkUsernameManuel ? 1 : 0,
'chk_user_delete_pass': amicale.chkUserDeletePass ? 1 : 0,
};
// Ajouter les champs réservés aux administrateurs si l'utilisateur est admin
@@ -401,6 +404,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
chkUserDeletePass: _chkUserDeletePass,
) ??
AmicaleModel(
id: 0, // Sera remplacé par l'API
@@ -424,6 +428,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
chkUserDeletePass: _chkUserDeletePass,
);
debugPrint('🔧 AmicaleModel créé: ${amicale.name}');
@@ -1159,6 +1164,20 @@ class _AmicaleFormState extends State<AmicaleForm> {
),
],
),
const SizedBox(height: 8),
// Checkbox pour autoriser les membres à supprimer des passages
_buildCheckboxOption(
label: "Autoriser les membres à supprimer des passages",
value: _chkUserDeletePass,
onChanged: widget.readOnly
? null
: (value) {
setState(() {
_chkUserDeletePass = value!;
});
},
),
const SizedBox(height: 25),
// Boutons Fermer et Enregistrer

View File

@@ -5,10 +5,8 @@ import 'package:geosector_app/core/services/app_info_service.dart';
import 'package:geosector_app/core/services/current_amicale_service.dart';
import 'package:geosector_app/presentation/widgets/connectivity_indicator.dart';
import 'package:geosector_app/presentation/widgets/user_form_dialog.dart';
import 'package:geosector_app/presentation/widgets/passage_form_dialog.dart';
import 'package:geosector_app/core/utils/api_exception.dart';
import 'package:geosector_app/core/services/theme_service.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:go_router/go_router.dart';
/// AppBar personnalisée pour les tableaux de bord
@@ -19,12 +17,6 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
/// Le titre de la page actuelle (optionnel)
final String? pageTitle;
/// Indique si le bouton "Nouveau passage" doit être affiché
final bool showNewPassageButton;
/// Callback appelé lorsque le bouton "Nouveau passage" est pressé
final VoidCallback? onNewPassagePressed;
/// Indique si l'utilisateur est un administrateur
final bool isAdmin;
@@ -35,8 +27,6 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
super.key,
required this.title,
this.pageTitle,
this.showNewPassageButton = true,
this.onNewPassagePressed,
this.isAdmin = false,
this.onLogoutPressed,
});
@@ -166,42 +156,6 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
actions.add(const SizedBox(width: 8));
// Ajouter le bouton "Nouveau passage" seulement si l'utilisateur n'est pas admin
if (!isAdmin) {
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: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (dialogContext) => PassageFormDialog(
title: 'Nouveau passage',
passageRepository: passageRepository,
userRepository: userRepository,
operationRepository: operationRepository,
onSuccess: () {
// Callback après création du passage
if (onNewPassagePressed != null) {
onNewPassagePressed!();
}
},
),
);
},
style: TextButton.styleFrom(
backgroundColor: Color(AppKeys.typesPassages[1]!['couleur1']
as int), // Vert des passages effectués
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
);
actions.add(const SizedBox(width: 8));
}
// Ajouter le sélecteur de thème avec confirmation (désactivé temporairement)
// TODO: Réactiver quand le thème sombre sera corrigé
// actions.add(

View File

@@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:geosector_app/presentation/widgets/dashboard_app_bar.dart';
import 'package:geosector_app/presentation/widgets/responsive_navigation.dart';
import 'package:geosector_app/app.dart'; // Pour accéder à userRepository
import 'package:geosector_app/core/theme/app_theme.dart'; // Pour les couleurs du thème
import 'dart:math' as math;
/// Layout commun pour les tableaux de bord utilisateur et administrateur
/// Combine DashboardAppBar et ResponsiveNavigation
@@ -23,12 +26,6 @@ class DashboardLayout extends StatelessWidget {
/// Actions supplémentaires à afficher dans l'AppBar
final List<Widget>? additionalActions;
/// Indique si le bouton "Nouveau passage" doit être affiché
final bool showNewPassageButton;
/// Callback appelé lorsque le bouton "Nouveau passage" est pressé
final VoidCallback? onNewPassagePressed;
/// Widgets à afficher en bas de la sidebar
final List<Widget>? sidebarBottomItems;
@@ -46,8 +43,6 @@ class DashboardLayout extends StatelessWidget {
required this.onDestinationSelected,
required this.destinations,
this.additionalActions,
this.showNewPassageButton = true,
this.onNewPassagePressed,
this.sidebarBottomItems,
this.isAdmin = false,
this.onLogoutPressed,
@@ -79,32 +74,57 @@ class DashboardLayout extends StatelessWidget {
);
}
return Scaffold(
backgroundColor: Colors
.transparent, // Fond transparent pour laisser voir le AdminBackground
appBar: DashboardAppBar(
title: title,
pageTitle: destinations[selectedIndex].label,
showNewPassageButton: showNewPassageButton,
onNewPassagePressed: onNewPassagePressed,
isAdmin: isAdmin,
onLogoutPressed: onLogoutPressed,
),
body: ResponsiveNavigation(
title:
title, // Même si le titre n'est pas affiché dans la navigation, il est utilisé pour la cohérence
body: body,
selectedIndex: selectedIndex,
onDestinationSelected: onDestinationSelected,
destinations: destinations,
// Ne pas afficher le bouton "Nouveau passage" dans la navigation car il est déjà dans l'AppBar
showNewPassageButton: false,
onNewPassagePressed: onNewPassagePressed,
sidebarBottomItems: sidebarBottomItems,
isAdmin: isAdmin,
// Ne pas afficher l'AppBar dans la navigation car nous utilisons DashboardAppBar
showAppBar: false,
),
// Déterminer le rôle de l'utilisateur
final currentUser = userRepository.getCurrentUser();
final userRole = currentUser?.role ?? 1;
// Définir les couleurs du gradient selon le rôle
final gradientColors = userRole > 1
? [Colors.white, Colors.red.shade300] // Admin : fond rouge
: [Colors.white, AppTheme.accentColor.withOpacity(0.3)]; // User : fond vert
return Stack(
children: [
// Fond dégradé avec points
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: gradientColors,
),
),
child: CustomPaint(
painter: DotsPainter(),
child: const SizedBox.expand(),
),
),
// Scaffold avec fond transparent
Scaffold(
backgroundColor: Colors.transparent,
appBar: DashboardAppBar(
title: title,
pageTitle: destinations[selectedIndex].label,
isAdmin: isAdmin,
onLogoutPressed: onLogoutPressed,
),
body: ResponsiveNavigation(
title:
title, // Même si le titre n'est pas affiché dans la navigation, il est utilisé pour la cohérence
body: body,
selectedIndex: selectedIndex,
onDestinationSelected: onDestinationSelected,
destinations: destinations,
// Ne pas afficher le bouton "Nouveau passage" dans la navigation
showNewPassageButton: false,
onNewPassagePressed: null,
sidebarBottomItems: sidebarBottomItems,
isAdmin: isAdmin,
// Ne pas afficher l'AppBar dans la navigation car nous utilisons DashboardAppBar
showAppBar: false,
),
),
],
);
} catch (e) {
debugPrint('ERREUR CRITIQUE dans DashboardLayout.build: $e');
@@ -141,3 +161,26 @@ class DashboardLayout extends StatelessWidget {
}
}
}
/// CustomPainter pour dessiner les petits points blancs sur le fond
class DotsPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white.withOpacity(0.5)
..style = PaintingStyle.fill;
final random = math.Random(42); // Seed fixe pour consistance
final numberOfDots = (size.width * size.height) ~/ 1500;
for (int i = 0; i < numberOfDots; i++) {
final x = random.nextDouble() * size.width;
final y = random.nextDouble() * size.height;
final radius = 1.0 + random.nextDouble() * 2.0;
canvas.drawCircle(Offset(x, y), radius, paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -118,6 +118,7 @@ class _ResponsiveNavigationState extends State<ResponsiveNavigation> {
final isDesktop = size.width > 900;
return Scaffold(
backgroundColor: Colors.transparent, // Fond transparent pour voir le gradient
appBar: widget.showAppBar
? AppBar(
title: Text(widget.title),