diff --git a/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart b/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart index d248dce..9417eae 100644 --- a/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart +++ b/frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart @@ -9,12 +9,14 @@ class AdminUserFormDialog extends StatefulWidget { final AppUser? initialUser; final bool withRelais; final bool adminMode; + final bool readOnly; const AdminUserFormDialog({ super.key, this.initialUser, this.withRelais = true, this.adminMode = false, + this.readOnly = false, }); @override @@ -194,6 +196,7 @@ class _AdminUserFormDialogState extends State { } Future _submit() async { + if (widget.readOnly) return; if (_isSubmitting) return; if (!_formKey.currentState!.validate()) return; @@ -304,6 +307,7 @@ class _AdminUserFormDialogState extends State { } Future _delete() async { + if (widget.readOnly) return; if (!_isEditMode || _isSubmitting) return; final confirmed = await showDialog( @@ -363,15 +367,19 @@ class _AdminUserFormDialogState extends State { Expanded( child: Text( _isEditMode - ? (widget.adminMode - ? 'Modifier un administrateur' - : 'Modifier un gestionnaire') + ? (widget.readOnly + ? (widget.adminMode + ? 'Consulter un administrateur' + : 'Consulter un gestionnaire') + : (widget.adminMode + ? 'Modifier un administrateur' + : 'Modifier un gestionnaire')) : (widget.adminMode ? 'Créer un administrateur' : 'Créer un gestionnaire'), ), ), - if (_isEditMode) + if (_isEditMode && !widget.readOnly) IconButton( icon: const Icon(Icons.close), tooltip: 'Fermer', @@ -416,7 +424,12 @@ class _AdminUserFormDialogState extends State { ), ), actions: [ - if (_isEditMode) ...[ + if (widget.readOnly) ...[ + FilledButton( + onPressed: _isSubmitting ? null : () => Navigator.of(context).pop(false), + child: const Text('Fermer'), + ), + ] else if (_isEditMode) ...[ OutlinedButton( onPressed: _isSubmitting ? null : _delete, style: OutlinedButton.styleFrom(foregroundColor: Colors.red.shade700), @@ -458,42 +471,46 @@ class _AdminUserFormDialogState extends State { Widget _buildNomField() { return TextFormField( controller: _nomController, + readOnly: widget.readOnly, textCapitalization: TextCapitalization.words, decoration: const InputDecoration( labelText: 'Nom', border: OutlineInputBorder(), ), - validator: (v) => _required(v, 'Nom'), + validator: widget.readOnly ? null : (v) => _required(v, 'Nom'), ); } Widget _buildPrenomField() { return TextFormField( controller: _prenomController, + readOnly: widget.readOnly, textCapitalization: TextCapitalization.words, decoration: const InputDecoration( labelText: 'Prénom', border: OutlineInputBorder(), ), - validator: (v) => _required(v, 'Prénom'), + validator: widget.readOnly ? null : (v) => _required(v, 'Prénom'), ); } Widget _buildEmailField() { return TextFormField( controller: _emailController, + readOnly: widget.readOnly, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( labelText: 'Email', border: OutlineInputBorder(), ), - validator: _validateEmail, + validator: widget.readOnly ? null : _validateEmail, ); } Widget _buildPasswordField() { return TextFormField( controller: _passwordController, + readOnly: widget.readOnly, obscureText: _obscurePassword, enableSuggestions: false, autocorrect: false, @@ -505,38 +522,43 @@ class _AdminUserFormDialogState extends State { ? 'Nouveau mot de passe' : 'Mot de passe', border: const OutlineInputBorder(), - suffixIcon: ExcludeFocus( - child: IconButton( - focusNode: _passwordToggleFocusNode, - onPressed: () { - setState(() { - _obscurePassword = !_obscurePassword; - }); - }, - icon: Icon( - _obscurePassword ? Icons.visibility_off : Icons.visibility, - ), - ), - ), + suffixIcon: widget.readOnly + ? null + : ExcludeFocus( + child: IconButton( + focusNode: _passwordToggleFocusNode, + onPressed: () { + setState(() { + _obscurePassword = !_obscurePassword; + }); + }, + icon: Icon( + _obscurePassword ? Icons.visibility_off : Icons.visibility, + ), + ), + ), ), - validator: _validatePassword, + validator: widget.readOnly ? null : _validatePassword, ); } Widget _buildTelephoneField() { return TextFormField( controller: _telephoneController, + readOnly: widget.readOnly, keyboardType: TextInputType.phone, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(10), - _FrenchPhoneNumberFormatter(), - ], + inputFormatters: widget.readOnly + ? null + : [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + _FrenchPhoneNumberFormatter(), + ], decoration: const InputDecoration( labelText: 'Téléphone (ex: 06 12 34 56 78)', border: OutlineInputBorder(), ), - validator: _validatePhone, + validator: widget.readOnly ? null : _validatePhone, ); } @@ -568,7 +590,7 @@ class _AdminUserFormDialogState extends State { ), ), ], - onChanged: _isLoadingRelais + onChanged: (_isLoadingRelais || widget.readOnly) ? null : (value) { setState(() { diff --git a/frontend/lib/widgets/admin/admin_management_widget.dart b/frontend/lib/widgets/admin/admin_management_widget.dart index a8e2db5..2c73bdb 100644 --- a/frontend/lib/widgets/admin/admin_management_widget.dart +++ b/frontend/lib/widgets/admin/admin_management_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/user.dart'; import 'package:p_tits_pas/screens/administrateurs/creation/gestionnaires_create.dart'; +import 'package:p_tits_pas/services/auth_service.dart'; import 'package:p_tits_pas/services/user_service.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_user_card.dart'; import 'package:p_tits_pas/widgets/admin/common/user_list.dart'; @@ -21,10 +22,12 @@ class _AdminManagementWidgetState extends State { bool _isLoading = false; String? _error; List _admins = []; + String? _currentUserRole; @override void initState() { super.initState(); + _loadCurrentUserRole(); _loadAdmins(); } @@ -52,7 +55,31 @@ class _AdminManagementWidgetState extends State { } } + Future _loadCurrentUserRole() async { + final cached = await AuthService.getCurrentUser(); + if (!mounted) return; + if (cached != null) { + setState(() { + _currentUserRole = cached.role.toLowerCase(); + }); + return; + } + final refreshed = await AuthService.refreshCurrentUser(); + if (!mounted || refreshed == null) return; + setState(() { + _currentUserRole = refreshed.role.toLowerCase(); + }); + } + + bool _isSuperAdmin(AppUser user) => user.role.toLowerCase() == 'super_admin'; + + bool _canEditAdmin(AppUser target) { + if (!_isSuperAdmin(target)) return true; + return _currentUserRole == 'super_admin'; + } + Future _openAdminEditDialog(AppUser user) async { + final canEdit = _canEditAdmin(user); final changed = await showDialog( context: context, barrierDismissible: false, @@ -61,10 +88,11 @@ class _AdminManagementWidgetState extends State { initialUser: user, adminMode: true, withRelais: false, + readOnly: !canEdit, ); }, ); - if (changed == true) { + if (changed == true && canEdit) { await _loadAdmins(); } } @@ -86,6 +114,8 @@ class _AdminManagementWidgetState extends State { itemCount: filteredAdmins.length, itemBuilder: (context, index) { final user = filteredAdmins[index]; + final isSuperAdmin = _isSuperAdmin(user); + final canEdit = _canEditAdmin(user); return AdminUserCard( title: user.fullName, subtitleLines: [ @@ -93,10 +123,22 @@ class _AdminManagementWidgetState extends State { 'Rôle : ${user.role}', ], avatarUrl: user.photoUrl, + borderColor: isSuperAdmin + ? const Color(0xFF8E6AC8) + : Colors.grey.shade300, + backgroundColor: isSuperAdmin + ? const Color(0xFFF4EEFF) + : Colors.white, + titleColor: isSuperAdmin ? const Color(0xFF5D2F99) : null, + infoColor: isSuperAdmin + ? const Color(0xFF6D4EA1) + : Colors.black54, actions: [ IconButton( - icon: const Icon(Icons.edit), - tooltip: 'Modifier', + icon: Icon( + canEdit ? Icons.edit_outlined : Icons.visibility_outlined, + ), + tooltip: canEdit ? 'Modifier' : 'Consulter', onPressed: () { _openAdminEditDialog(user); }, diff --git a/frontend/lib/widgets/admin/common/admin_user_card.dart b/frontend/lib/widgets/admin/common/admin_user_card.dart index 914e218..92d4237 100644 --- a/frontend/lib/widgets/admin/common/admin_user_card.dart +++ b/frontend/lib/widgets/admin/common/admin_user_card.dart @@ -6,6 +6,10 @@ class AdminUserCard extends StatefulWidget { final String? avatarUrl; final IconData fallbackIcon; final List actions; + final Color? borderColor; + final Color? backgroundColor; + final Color? titleColor; + final Color? infoColor; const AdminUserCard({ super.key, @@ -14,6 +18,10 @@ class AdminUserCard extends StatefulWidget { this.avatarUrl, this.fallbackIcon = Icons.person, this.actions = const [], + this.borderColor, + this.backgroundColor, + this.titleColor, + this.infoColor, }); @override @@ -43,9 +51,10 @@ class _AdminUserCardState extends State { child: Card( margin: const EdgeInsets.only(bottom: 12), elevation: 0, + color: widget.backgroundColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), - side: BorderSide(color: Colors.grey.shade300), + side: BorderSide(color: widget.borderColor ?? Colors.grey.shade300), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9), @@ -76,7 +85,7 @@ class _AdminUserCardState extends State { style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 14, - ), + ).copyWith(color: widget.titleColor), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -88,7 +97,7 @@ class _AdminUserCardState extends State { style: const TextStyle( color: Colors.black54, fontSize: 12, - ), + ).copyWith(color: widget.infoColor), maxLines: 1, overflow: TextOverflow.ellipsis, ),