feat(#35): unifier la modale gestionnaire en création et édition
Branche la modale sur l'action Modifier, supprime l'action dédiée de rattachement relais, ajoute la suppression avec confirmation et sécurise le dropdown relais en édition. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
42bb872c41
commit
bb92f010bd
@ -1,10 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:p_tits_pas/models/relais_model.dart';
|
import 'package:p_tits_pas/models/relais_model.dart';
|
||||||
|
import 'package:p_tits_pas/models/user.dart';
|
||||||
import 'package:p_tits_pas/services/relais_service.dart';
|
import 'package:p_tits_pas/services/relais_service.dart';
|
||||||
import 'package:p_tits_pas/services/user_service.dart';
|
import 'package:p_tits_pas/services/user_service.dart';
|
||||||
|
|
||||||
class GestionnaireCreateDialog extends StatefulWidget {
|
class GestionnaireCreateDialog extends StatefulWidget {
|
||||||
const GestionnaireCreateDialog({super.key});
|
final AppUser? initialUser;
|
||||||
|
|
||||||
|
const GestionnaireCreateDialog({
|
||||||
|
super.key,
|
||||||
|
this.initialUser,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GestionnaireCreateDialog> createState() =>
|
State<GestionnaireCreateDialog> createState() =>
|
||||||
@ -24,10 +30,25 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
bool _isLoadingRelais = true;
|
bool _isLoadingRelais = true;
|
||||||
List<RelaisModel> _relais = [];
|
List<RelaisModel> _relais = [];
|
||||||
String? _selectedRelaisId;
|
String? _selectedRelaisId;
|
||||||
|
bool get _isEditMode => widget.initialUser != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
final user = widget.initialUser;
|
||||||
|
if (user != null) {
|
||||||
|
_nomController.text = user.nom ?? '';
|
||||||
|
_prenomController.text = user.prenom ?? '';
|
||||||
|
_emailController.text = user.email;
|
||||||
|
_telephoneController.text = user.telephone ?? '';
|
||||||
|
// En édition, on ne préremplit jamais le mot de passe.
|
||||||
|
_passwordController.clear();
|
||||||
|
final initialRelaisId = user.relaisId?.trim();
|
||||||
|
_selectedRelaisId =
|
||||||
|
(initialRelaisId == null || initialRelaisId.isEmpty)
|
||||||
|
? null
|
||||||
|
: initialRelaisId;
|
||||||
|
}
|
||||||
_loadRelais();
|
_loadRelais();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,13 +66,30 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
try {
|
try {
|
||||||
final list = await RelaisService.getRelais();
|
final list = await RelaisService.getRelais();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
final uniqueById = <String, RelaisModel>{};
|
||||||
|
for (final relais in list) {
|
||||||
|
uniqueById[relais.id] = relais;
|
||||||
|
}
|
||||||
|
|
||||||
|
final filtered = uniqueById.values.where((r) => r.actif).toList();
|
||||||
|
if (_selectedRelaisId != null &&
|
||||||
|
!filtered.any((r) => r.id == _selectedRelaisId)) {
|
||||||
|
final selected = uniqueById[_selectedRelaisId!];
|
||||||
|
if (selected != null) {
|
||||||
|
filtered.add(selected);
|
||||||
|
} else {
|
||||||
|
_selectedRelaisId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_relais = list.where((r) => r.actif).toList();
|
_relais = filtered;
|
||||||
_isLoadingRelais = false;
|
_isLoadingRelais = false;
|
||||||
});
|
});
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
|
_selectedRelaisId = null;
|
||||||
_relais = [];
|
_relais = [];
|
||||||
_isLoadingRelais = false;
|
_isLoadingRelais = false;
|
||||||
});
|
});
|
||||||
@ -75,6 +113,9 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String? _validatePassword(String? value) {
|
String? _validatePassword(String? value) {
|
||||||
|
if (_isEditMode && (value == null || value.trim().isEmpty)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final base = _required(value, 'Mot de passe');
|
final base = _required(value, 'Mot de passe');
|
||||||
if (base != null) return base;
|
if (base != null) return base;
|
||||||
if (value!.trim().length < 6) return 'Minimum 6 caractères';
|
if (value!.trim().length < 6) return 'Minimum 6 caractères';
|
||||||
@ -90,6 +131,19 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (_isEditMode) {
|
||||||
|
await UserService.updateGestionnaire(
|
||||||
|
gestionnaireId: widget.initialUser!.id,
|
||||||
|
nom: _nomController.text.trim(),
|
||||||
|
prenom: _prenomController.text.trim(),
|
||||||
|
email: _emailController.text.trim(),
|
||||||
|
telephone: _telephoneController.text.trim(),
|
||||||
|
relaisId: _selectedRelaisId,
|
||||||
|
password: _passwordController.text.trim().isEmpty
|
||||||
|
? null
|
||||||
|
: _passwordController.text,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
await UserService.createGestionnaire(
|
await UserService.createGestionnaire(
|
||||||
nom: _nomController.text.trim(),
|
nom: _nomController.text.trim(),
|
||||||
prenom: _prenomController.text.trim(),
|
prenom: _prenomController.text.trim(),
|
||||||
@ -98,9 +152,16 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
telephone: _telephoneController.text.trim(),
|
telephone: _telephoneController.text.trim(),
|
||||||
relaisId: _selectedRelaisId,
|
relaisId: _selectedRelaisId,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Gestionnaire créé avec succès.')),
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
_isEditMode
|
||||||
|
? 'Gestionnaire modifié avec succès.'
|
||||||
|
: 'Gestionnaire créé avec succès.',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -121,19 +182,154 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _delete() async {
|
||||||
|
if (!_isEditMode || _isSubmitting) return;
|
||||||
|
|
||||||
|
final confirmed = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Confirmer la suppression'),
|
||||||
|
content: Text(
|
||||||
|
'Supprimer ${widget.initialUser!.fullName.isEmpty ? widget.initialUser!.email : widget.initialUser!.fullName} ?',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(ctx).pop(false),
|
||||||
|
child: const Text('Annuler'),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () => Navigator.of(ctx).pop(true),
|
||||||
|
style: FilledButton.styleFrom(backgroundColor: Colors.red.shade700),
|
||||||
|
child: const Text('Supprimer'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed != true) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isSubmitting = true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await UserService.deleteUser(widget.initialUser!.id);
|
||||||
|
if (!mounted) return;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Gestionnaire supprimé.')),
|
||||||
|
);
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(e.toString().replaceFirst('Exception: ', '')),
|
||||||
|
backgroundColor: Colors.red.shade700,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
_isSubmitting = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Créer un gestionnaire'),
|
title: Row(
|
||||||
content: ConstrainedBox(
|
children: [
|
||||||
constraints: const BoxConstraints(maxWidth: 640),
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_isEditMode
|
||||||
|
? 'Modifier un gestionnaire'
|
||||||
|
: 'Créer un gestionnaire',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_isEditMode)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
tooltip: 'Fermer',
|
||||||
|
onPressed: _isSubmitting
|
||||||
|
? null
|
||||||
|
: () => Navigator.of(context).pop(false),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: SizedBox(
|
||||||
|
width: 620,
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
TextFormField(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildNomField()),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(child: _buildPrenomField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildEmailField(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildPasswordField()),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(child: _buildTelephoneField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildRelaisField(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
if (_isEditMode) ...[
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed: _isSubmitting ? null : _delete,
|
||||||
|
style: OutlinedButton.styleFrom(foregroundColor: Colors.red.shade700),
|
||||||
|
child: const Text('Supprimer'),
|
||||||
|
),
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: _isSubmitting ? null : _submit,
|
||||||
|
icon: _isSubmitting
|
||||||
|
? const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
)
|
||||||
|
: const Icon(Icons.edit),
|
||||||
|
label: Text(_isSubmitting ? 'Modification...' : 'Modifier'),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed:
|
||||||
|
_isSubmitting ? null : () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text('Annuler'),
|
||||||
|
),
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: _isSubmitting ? null : _submit,
|
||||||
|
icon: _isSubmitting
|
||||||
|
? const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
)
|
||||||
|
: const Icon(Icons.person_add_alt_1),
|
||||||
|
label: Text(_isSubmitting ? 'Création...' : 'Créer'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildNomField() {
|
||||||
|
return TextFormField(
|
||||||
controller: _nomController,
|
controller: _nomController,
|
||||||
textCapitalization: TextCapitalization.words,
|
textCapitalization: TextCapitalization.words,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@ -141,9 +337,11 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
validator: (v) => _required(v, 'Nom'),
|
validator: (v) => _required(v, 'Nom'),
|
||||||
),
|
);
|
||||||
const SizedBox(height: 12),
|
}
|
||||||
TextFormField(
|
|
||||||
|
Widget _buildPrenomField() {
|
||||||
|
return TextFormField(
|
||||||
controller: _prenomController,
|
controller: _prenomController,
|
||||||
textCapitalization: TextCapitalization.words,
|
textCapitalization: TextCapitalization.words,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@ -151,9 +349,11 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
validator: (v) => _required(v, 'Prénom'),
|
validator: (v) => _required(v, 'Prénom'),
|
||||||
),
|
);
|
||||||
const SizedBox(height: 12),
|
}
|
||||||
TextFormField(
|
|
||||||
|
Widget _buildEmailField() {
|
||||||
|
return TextFormField(
|
||||||
controller: _emailController,
|
controller: _emailController,
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@ -161,13 +361,22 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
validator: _validateEmail,
|
validator: _validateEmail,
|
||||||
),
|
);
|
||||||
const SizedBox(height: 12),
|
}
|
||||||
TextFormField(
|
|
||||||
|
Widget _buildPasswordField() {
|
||||||
|
return TextFormField(
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: _obscurePassword,
|
obscureText: _obscurePassword,
|
||||||
|
enableSuggestions: false,
|
||||||
|
autocorrect: false,
|
||||||
|
autofillHints: _isEditMode
|
||||||
|
? const <String>[]
|
||||||
|
: const [AutofillHints.newPassword],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Mot de passe',
|
labelText: _isEditMode
|
||||||
|
? 'Nouveau mot de passe'
|
||||||
|
: 'Mot de passe',
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -176,16 +385,16 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_obscurePassword
|
_obscurePassword ? Icons.visibility_off : Icons.visibility,
|
||||||
? Icons.visibility_off
|
|
||||||
: Icons.visibility,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
validator: _validatePassword,
|
validator: _validatePassword,
|
||||||
),
|
);
|
||||||
const SizedBox(height: 12),
|
}
|
||||||
TextFormField(
|
|
||||||
|
Widget _buildTelephoneField() {
|
||||||
|
return TextFormField(
|
||||||
controller: _telephoneController,
|
controller: _telephoneController,
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@ -193,10 +402,21 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
validator: (v) => _required(v, 'Téléphone'),
|
validator: (v) => _required(v, 'Téléphone'),
|
||||||
),
|
);
|
||||||
const SizedBox(height: 12),
|
}
|
||||||
|
|
||||||
|
Widget _buildRelaisField() {
|
||||||
|
final selectedValue = _selectedRelaisId != null &&
|
||||||
|
_relais.any((relais) => relais.id == _selectedRelaisId)
|
||||||
|
? _selectedRelaisId
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
DropdownButtonFormField<String?>(
|
DropdownButtonFormField<String?>(
|
||||||
value: _selectedRelaisId,
|
isExpanded: true,
|
||||||
|
value: selectedValue,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Relais principal',
|
labelText: 'Relais principal',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
@ -226,27 +446,6 @@ class _GestionnaireCreateDialogState extends State<GestionnaireCreateDialog> {
|
|||||||
const LinearProgressIndicator(minHeight: 2),
|
const LinearProgressIndicator(minHeight: 2),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
OutlinedButton(
|
|
||||||
onPressed: _isSubmitting ? null : () => Navigator.of(context).pop(false),
|
|
||||||
child: const Text('Annuler'),
|
|
||||||
),
|
|
||||||
FilledButton.icon(
|
|
||||||
onPressed: _isSubmitting ? null : _submit,
|
|
||||||
icon: _isSubmitting
|
|
||||||
? const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
|
||||||
)
|
|
||||||
: const Icon(Icons.person_add_alt_1),
|
|
||||||
label: Text(_isSubmitting ? 'Création...' : 'Créer'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,4 +150,66 @@ class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<AppUser> updateGestionnaire({
|
||||||
|
required String gestionnaireId,
|
||||||
|
required String nom,
|
||||||
|
required String prenom,
|
||||||
|
required String email,
|
||||||
|
required String telephone,
|
||||||
|
required String? relaisId,
|
||||||
|
String? password,
|
||||||
|
}) async {
|
||||||
|
final body = <String, dynamic>{
|
||||||
|
'nom': nom,
|
||||||
|
'prenom': prenom,
|
||||||
|
'email': email,
|
||||||
|
'telephone': telephone,
|
||||||
|
'relaisId': relaisId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (password != null && password.trim().isNotEmpty) {
|
||||||
|
body['password'] = password.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.patch(
|
||||||
|
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.gestionnaires}/$gestionnaireId'),
|
||||||
|
headers: await _headers(),
|
||||||
|
body: jsonEncode(body),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
final decoded = jsonDecode(response.body);
|
||||||
|
if (decoded is Map<String, dynamic>) {
|
||||||
|
final message = decoded['message'];
|
||||||
|
if (message is List && message.isNotEmpty) {
|
||||||
|
throw Exception(message.join(' - '));
|
||||||
|
}
|
||||||
|
throw Exception(_toStr(message) ?? 'Erreur modification gestionnaire');
|
||||||
|
}
|
||||||
|
throw Exception('Erreur modification gestionnaire');
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
|
return AppUser.fromJson(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> deleteUser(String userId) async {
|
||||||
|
final response = await http.delete(
|
||||||
|
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.users}/$userId'),
|
||||||
|
headers: await _headers(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 200 && response.statusCode != 204) {
|
||||||
|
final decoded = jsonDecode(response.body);
|
||||||
|
if (decoded is Map<String, dynamic>) {
|
||||||
|
final message = decoded['message'];
|
||||||
|
if (message is List && message.isNotEmpty) {
|
||||||
|
throw Exception(message.join(' - '));
|
||||||
|
}
|
||||||
|
throw Exception(_toStr(message) ?? 'Erreur suppression utilisateur');
|
||||||
|
}
|
||||||
|
throw Exception('Erreur suppression utilisateur');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:p_tits_pas/models/relais_model.dart';
|
|
||||||
import 'package:p_tits_pas/models/user.dart';
|
import 'package:p_tits_pas/models/user.dart';
|
||||||
import 'package:p_tits_pas/services/relais_service.dart';
|
import 'package:p_tits_pas/screens/administrateurs/creation/gestionnaires_create.dart';
|
||||||
import 'package:p_tits_pas/services/user_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/admin_user_card.dart';
|
||||||
import 'package:p_tits_pas/widgets/admin/common/user_list.dart';
|
import 'package:p_tits_pas/widgets/admin/common/user_list.dart';
|
||||||
@ -24,7 +23,6 @@ class _GestionnaireManagementWidgetState
|
|||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
String? _error;
|
String? _error;
|
||||||
List<AppUser> _gestionnaires = [];
|
List<AppUser> _gestionnaires = [];
|
||||||
List<RelaisModel> _relais = [];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -42,17 +40,9 @@ class _GestionnaireManagementWidgetState
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
final gestionnaires = await UserService.getGestionnaires();
|
final gestionnaires = await UserService.getGestionnaires();
|
||||||
List<RelaisModel> relais = [];
|
|
||||||
try {
|
|
||||||
relais = await RelaisService.getRelais();
|
|
||||||
} catch (_) {
|
|
||||||
// L'ecran reste utilisable meme si la route Relais n'est pas disponible.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_gestionnaires = gestionnaires;
|
_gestionnaires = gestionnaires;
|
||||||
_relais = relais;
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -64,81 +54,16 @@ class _GestionnaireManagementWidgetState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openRelaisAssignmentDialog(AppUser user) async {
|
Future<void> _openGestionnaireEditDialog(AppUser user) async {
|
||||||
String? selectedRelaisId = user.relaisId;
|
final changed = await showDialog<bool>(
|
||||||
final saved = await showDialog<bool>(
|
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) {
|
barrierDismissible: false,
|
||||||
return StatefulBuilder(
|
builder: (dialogContext) {
|
||||||
builder: (context, setStateDialog) {
|
return GestionnaireCreateDialog(initialUser: user);
|
||||||
return AlertDialog(
|
|
||||||
title: Text(
|
|
||||||
'Rattacher ${user.fullName.isEmpty ? user.email : user.fullName}',
|
|
||||||
),
|
|
||||||
content: DropdownButtonFormField<String?>(
|
|
||||||
value: selectedRelaisId,
|
|
||||||
isExpanded: true,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Relais principal',
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
items: [
|
|
||||||
const DropdownMenuItem<String?>(
|
|
||||||
value: null,
|
|
||||||
child: Text('Aucun relais'),
|
|
||||||
),
|
|
||||||
..._relais.map(
|
|
||||||
(relais) => DropdownMenuItem<String?>(
|
|
||||||
value: relais.id,
|
|
||||||
child: Text(relais.nom),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
setStateDialog(() {
|
|
||||||
selectedRelaisId = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(ctx).pop(false),
|
|
||||||
child: const Text('Annuler'),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () => Navigator.of(ctx).pop(true),
|
|
||||||
child: const Text('Enregistrer'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
if (changed == true) {
|
||||||
);
|
|
||||||
|
|
||||||
if (saved != true) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await UserService.updateGestionnaireRelais(
|
|
||||||
gestionnaireId: user.id,
|
|
||||||
relaisId: selectedRelaisId,
|
|
||||||
);
|
|
||||||
if (!mounted) return;
|
|
||||||
await _loadGestionnaires();
|
await _loadGestionnaires();
|
||||||
if (!mounted) return;
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(content: Text('Rattachement relais mis a jour.')),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (!mounted) return;
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
e.toString().replaceAll('Exception: ', ''),
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.red.shade600,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,16 +93,11 @@ class _GestionnaireManagementWidgetState
|
|||||||
'Relais : ${user.relaisNom ?? 'Non rattaché'}',
|
'Relais : ${user.relaisNom ?? 'Non rattaché'}',
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.location_city_outlined),
|
|
||||||
tooltip: 'Rattacher un relais',
|
|
||||||
onPressed: () => _openRelaisAssignmentDialog(user),
|
|
||||||
),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
tooltip: 'Modifier',
|
tooltip: 'Modifier',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// TODO: Modifier gestionnaire.
|
_openGestionnaireEditDialog(user);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user