feat(#138,#143,#144): fiche enfant admin, consentement photo et fix gestionnaire.
Squash merge develop → master. - #138 : modale enfant paysage, zone AM/scolarisation, liens responsables - #144 : persistance consent_photo à l'inscription enfant - #143 : masquer Supprimer sur la propre fiche gestionnaire Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d6dac181d2
commit
1f8f1b9507
111
backend/scripts/create-gitea-issue-gestionnaire-self-delete.js
Normal file
111
backend/scripts/create-gitea-issue-gestionnaire-self-delete.js
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Crée l'issue Gitea — gestionnaire ne doit pas pouvoir se supprimer.
|
||||
* Usage: node backend/scripts/create-gitea-issue-gestionnaire-self-delete.js
|
||||
*/
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const repoRoot = path.join(__dirname, '../..');
|
||||
const MILESTONE_0_1_0 = 10;
|
||||
|
||||
let token = process.env.GITEA_TOKEN;
|
||||
if (!token) {
|
||||
try {
|
||||
const tokenFile = path.join(repoRoot, '.gitea-token');
|
||||
if (fs.existsSync(tokenFile)) token = fs.readFileSync(tokenFile, 'utf8').trim();
|
||||
} catch (_) {}
|
||||
}
|
||||
if (!token) {
|
||||
try {
|
||||
const briefing = fs.readFileSync(
|
||||
path.join(repoRoot, 'docs/27_BRIEFING-FRONTEND.md'),
|
||||
'utf8',
|
||||
);
|
||||
const m = briefing.match(/Token:\s*(gitebu_[a-f0-9]+)/);
|
||||
if (m) token = m[1].trim();
|
||||
} catch (_) {}
|
||||
}
|
||||
if (!token) {
|
||||
console.error('Token non trouvé : .gitea-token ou GITEA_TOKEN');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const body = `## Contexte
|
||||
|
||||
Quand un **gestionnaire** connecté ouvre sa propre fiche dans l'onglet **Gestionnaires** (Gestion des utilisateurs), la modale **Modifier un "Gestionnaire"** affiche le bouton **Supprimer**.
|
||||
|
||||
Comportement actuel : le gestionnaire voit et peut tenter de supprimer son propre compte.
|
||||
|
||||
## Comportement attendu
|
||||
|
||||
Comme pour le **super administrateur** (bouton Supprimer masqué sur la fiche super admin) :
|
||||
|
||||
- **Pas de bouton Supprimer** quand l'utilisateur édite **sa propre fiche**
|
||||
- **Modification** des informations (prénom, nom, email, téléphone, relais, mot de passe) **toujours autorisée**
|
||||
|
||||
## Périmètre
|
||||
|
||||
- Frontend : \`AdminUserFormDialog\` (\`gestionnaires_create.dart\`)
|
||||
- Comparer \`initialUser.id\` avec l'utilisateur connecté (\`AuthService.getCurrentUser\`)
|
||||
- Masquer Supprimer si édition de soi-même ; conserver garde existante super admin
|
||||
|
||||
## Critères d'acceptation
|
||||
|
||||
- [ ] Gestionnaire connecté → ouvre sa fiche → **pas** de bouton Supprimer
|
||||
- [ ] Gestionnaire connecté → peut **Modifier** ses informations
|
||||
- [ ] Super admin / admin → peut toujours supprimer **un autre** gestionnaire (si droits API)
|
||||
- [ ] Pas de régression sur fiche super administrateur (Supprimer toujours masqué)
|
||||
|
||||
## Fichiers clés
|
||||
|
||||
- \`frontend/lib/screens/administrateurs/creation/gestionnaires_create.dart\`
|
||||
- \`frontend/lib/widgets/admin/gestionnaire_management_widget.dart\`
|
||||
|
||||
## Milestone
|
||||
|
||||
**0.1.0** — correction UX / sécurité gestion utilisateurs.`;
|
||||
|
||||
const payloadClean = JSON.stringify({
|
||||
title: '[Bug] Gestionnaire peut voir Supprimer sur sa propre fiche',
|
||||
body,
|
||||
milestone: MILESTONE_0_1_0,
|
||||
});
|
||||
|
||||
const opts = {
|
||||
hostname: 'git.ptits-pas.fr',
|
||||
path: '/api/v1/repos/jmartin/petitspas/issues',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'token ' + token,
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(payloadClean),
|
||||
},
|
||||
};
|
||||
|
||||
const req = https.request(opts, (res) => {
|
||||
let d = '';
|
||||
res.on('data', (c) => (d += c));
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const o = JSON.parse(d);
|
||||
if (o.number) {
|
||||
console.log('NUMBER:', o.number);
|
||||
console.log('URL:', o.html_url);
|
||||
console.log('MILESTONE:', o.milestone?.title ?? '(aucun)');
|
||||
} else {
|
||||
console.error('Réponse:', d);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(d);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', (e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
req.write(payloadClean);
|
||||
req.end();
|
||||
@ -552,7 +552,8 @@ export class AuthService {
|
||||
: undefined;
|
||||
enfant.photo_url = urlPhoto || undefined;
|
||||
enfant.status = enfantDto.date_naissance ? StatutEnfantType.SANS_GARDE : StatutEnfantType.A_NAITRE;
|
||||
enfant.consent_photo = false;
|
||||
enfant.consent_photo = !!enfantDto.consent_photo;
|
||||
enfant.consent_photo_at = enfant.consent_photo ? new Date() : null!;
|
||||
enfant.is_multiple = enfantDto.grossesse_multiple || false;
|
||||
|
||||
const enfantEnregistre = await manager.save(Children, enfant);
|
||||
@ -1074,6 +1075,12 @@ export class AuthService {
|
||||
if (enfantDto.grossesse_multiple !== undefined) {
|
||||
enfant.is_multiple = enfantDto.grossesse_multiple;
|
||||
}
|
||||
if (enfantDto.consent_photo !== undefined) {
|
||||
enfant.consent_photo = !!enfantDto.consent_photo;
|
||||
enfant.consent_photo_at = enfant.consent_photo
|
||||
? new Date()
|
||||
: null!;
|
||||
}
|
||||
|
||||
if (enfantDto.photo_base64 && enfantDto.photo_filename) {
|
||||
enfant.photo_url = await this.sauvegarderPhotoDepuisBase64(
|
||||
|
||||
@ -59,5 +59,14 @@ export class EnfantInscriptionDto {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
grossesse_multiple?: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
required: false,
|
||||
description: 'Consentement affichage / stockage photo (colonne enfants.consentement_photo)',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
consent_photo?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@ -120,7 +120,13 @@ export class EnfantsService {
|
||||
const child = await this.childrenRepository.findOne({ where: { id } });
|
||||
if (!child) throw new NotFoundException('Enfant introuvable');
|
||||
|
||||
await this.childrenRepository.update(id, dto);
|
||||
const patch: Partial<Children> = { ...dto } as Partial<Children>;
|
||||
if (dto.consent_photo !== undefined) {
|
||||
patch.consent_photo = dto.consent_photo;
|
||||
patch.consent_photo_at = dto.consent_photo ? new Date() : null!;
|
||||
}
|
||||
|
||||
await this.childrenRepository.update(id, patch);
|
||||
return this.findOne(id, currentUser);
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,34 @@ class EnfantAdminModel {
|
||||
return '$fn $ln';
|
||||
}
|
||||
|
||||
EnfantAdminModel copyWith({
|
||||
String? id,
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
String? gender,
|
||||
String? birthDate,
|
||||
String? dueDate,
|
||||
String? status,
|
||||
String? photoUrl,
|
||||
bool? consentPhoto,
|
||||
bool? isMultiple,
|
||||
List<EnfantParentLink>? parentLinks,
|
||||
}) {
|
||||
return EnfantAdminModel(
|
||||
id: id ?? this.id,
|
||||
firstName: firstName ?? this.firstName,
|
||||
lastName: lastName ?? this.lastName,
|
||||
gender: gender ?? this.gender,
|
||||
birthDate: birthDate ?? this.birthDate,
|
||||
dueDate: dueDate ?? this.dueDate,
|
||||
status: status ?? this.status,
|
||||
photoUrl: photoUrl ?? this.photoUrl,
|
||||
consentPhoto: consentPhoto ?? this.consentPhoto,
|
||||
isMultiple: isMultiple ?? this.isMultiple,
|
||||
parentLinks: parentLinks ?? this.parentLinks,
|
||||
);
|
||||
}
|
||||
|
||||
factory EnfantAdminModel.fromJson(Map<String, dynamic> json) {
|
||||
final linksRaw = json['parentLinks'] as List?;
|
||||
final links = <EnfantParentLink>[];
|
||||
@ -48,17 +76,25 @@ class EnfantAdminModel {
|
||||
}
|
||||
}
|
||||
|
||||
final photoUrl = json['photo_url'] as String? ?? json['photoUrl'] as String?;
|
||||
final consentPhoto = _parseBool(json['consent_photo']) ||
|
||||
_parseBool(json['consentement_photo']) ||
|
||||
_parseBool(json['consentPhoto']);
|
||||
|
||||
return EnfantAdminModel(
|
||||
id: (json['id'] ?? '').toString(),
|
||||
firstName: json['first_name'] as String?,
|
||||
lastName: json['last_name'] as String?,
|
||||
gender: json['gender'] as String?,
|
||||
birthDate: _dateString(json['birth_date']),
|
||||
dueDate: _dateString(json['due_date']),
|
||||
status: normalizeEnfantStatus(json['status']?.toString()),
|
||||
photoUrl: json['photo_url'] as String?,
|
||||
consentPhoto: json['consent_photo'] == true,
|
||||
isMultiple: json['is_multiple'] == true,
|
||||
firstName: json['first_name'] as String? ?? json['prenom'] as String?,
|
||||
lastName: json['last_name'] as String? ?? json['nom'] as String?,
|
||||
gender: json['gender'] as String? ?? json['genre'] as String?,
|
||||
birthDate: _dateString(json['birth_date'] ?? json['date_naissance']),
|
||||
dueDate: _dateString(json['due_date'] ?? json['date_prevue_naissance']),
|
||||
status: normalizeEnfantStatus(
|
||||
(json['status'] ?? json['statut'])?.toString(),
|
||||
),
|
||||
photoUrl: photoUrl,
|
||||
consentPhoto: consentPhoto,
|
||||
isMultiple: _parseBool(json['is_multiple']) ||
|
||||
_parseBool(json['est_multiple']),
|
||||
parentLinks: links,
|
||||
);
|
||||
}
|
||||
@ -76,6 +112,15 @@ class EnfantAdminModel {
|
||||
};
|
||||
}
|
||||
|
||||
static bool _parseBool(dynamic value) {
|
||||
if (value == true || value == 1) return true;
|
||||
if (value is String) {
|
||||
final s = value.trim().toLowerCase();
|
||||
return s == 'true' || s == '1' || s == 't' || s == 'yes';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static String? _dateString(dynamic v) {
|
||||
if (v == null) return null;
|
||||
if (v is String) return v.split('T').first;
|
||||
@ -89,6 +134,13 @@ class EnfantParentLink {
|
||||
|
||||
EnfantParentLink({required this.parentId, this.parentName});
|
||||
|
||||
EnfantParentLink copyWith({String? parentId, String? parentName}) {
|
||||
return EnfantParentLink(
|
||||
parentId: parentId ?? this.parentId,
|
||||
parentName: parentName ?? this.parentName,
|
||||
);
|
||||
}
|
||||
|
||||
factory EnfantParentLink.fromJson(Map<String, dynamic> json) {
|
||||
final parentId =
|
||||
(json['parentId'] ?? json['id_parent'] ?? '').toString();
|
||||
@ -99,6 +151,12 @@ class EnfantParentLink {
|
||||
if (user is Map<String, dynamic>) {
|
||||
final u = AppUser.fromJson(user);
|
||||
name = u.fullName.isNotEmpty ? u.fullName : u.email;
|
||||
} else {
|
||||
// Parfois le parent est aplati (prenom/nom) sans nested user.
|
||||
final prenom = (parent['prenom'] ?? parent['first_name'] ?? '').toString().trim();
|
||||
final nom = (parent['nom'] ?? parent['last_name'] ?? '').toString().trim();
|
||||
final flat = '$prenom $nom'.trim();
|
||||
if (flat.isNotEmpty) name = flat;
|
||||
}
|
||||
}
|
||||
return EnfantParentLink(
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:p_tits_pas/utils/email_utils.dart';
|
||||
import 'package:p_tits_pas/widgets/email_text_field.dart';
|
||||
import 'package:p_tits_pas/widgets/french_phone_field.dart';
|
||||
import 'package:p_tits_pas/models/user.dart';
|
||||
import 'package:p_tits_pas/services/auth_service.dart';
|
||||
import 'package:p_tits_pas/services/relais_service.dart';
|
||||
import 'package:p_tits_pas/services/user_service.dart';
|
||||
|
||||
@ -41,9 +42,15 @@ class _AdminUserFormDialogState extends State<AdminUserFormDialog> {
|
||||
bool _isLoadingRelais = true;
|
||||
List<RelaisModel> _relais = [];
|
||||
String? _selectedRelaisId;
|
||||
String? _currentUserId;
|
||||
bool get _isEditMode => widget.initialUser != null;
|
||||
bool get _isSuperAdminTarget =>
|
||||
(widget.initialUser?.role ?? '').toLowerCase() == 'super_admin';
|
||||
bool get _isSelfTarget =>
|
||||
_isEditMode &&
|
||||
_currentUserId != null &&
|
||||
widget.initialUser!.id == _currentUserId;
|
||||
bool get _canDeleteTarget => !_isSuperAdminTarget && !_isSelfTarget;
|
||||
bool get _isLockedAdminIdentity =>
|
||||
_isEditMode && widget.adminMode && _isSuperAdminTarget;
|
||||
String get _targetRoleKey {
|
||||
@ -109,6 +116,23 @@ class _AdminUserFormDialogState extends State<AdminUserFormDialog> {
|
||||
} else {
|
||||
_isLoadingRelais = false;
|
||||
}
|
||||
_loadCurrentUserId();
|
||||
}
|
||||
|
||||
Future<void> _loadCurrentUserId() async {
|
||||
final cached = await AuthService.getCurrentUser();
|
||||
if (!mounted) return;
|
||||
if (cached != null) {
|
||||
setState(() {
|
||||
_currentUserId = cached.id;
|
||||
});
|
||||
return;
|
||||
}
|
||||
final refreshed = await AuthService.refreshCurrentUser();
|
||||
if (!mounted || refreshed == null) return;
|
||||
setState(() {
|
||||
_currentUserId = refreshed.id;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -335,7 +359,7 @@ class _AdminUserFormDialogState extends State<AdminUserFormDialog> {
|
||||
|
||||
Future<void> _delete() async {
|
||||
if (widget.readOnly) return;
|
||||
if (_isSuperAdminTarget) return;
|
||||
if (!_canDeleteTarget) return;
|
||||
if (!_isEditMode || _isSubmitting) return;
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
@ -462,7 +486,7 @@ class _AdminUserFormDialogState extends State<AdminUserFormDialog> {
|
||||
child: const Text('Fermer'),
|
||||
),
|
||||
] else if (_isEditMode) ...[
|
||||
if (!_isSuperAdminTarget)
|
||||
if (_canDeleteTarget)
|
||||
OutlinedButton(
|
||||
onPressed: _isSubmitting ? null : _delete,
|
||||
style: OutlinedButton.styleFrom(foregroundColor: Colors.red.shade700),
|
||||
|
||||
@ -460,7 +460,40 @@ class UserService {
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception(_extractErrorMessage(response.body, 'Erreur chargement enfant'));
|
||||
}
|
||||
return EnfantAdminModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
|
||||
final enfant = EnfantAdminModel.fromJson(
|
||||
jsonDecode(response.body) as Map<String, dynamic>,
|
||||
);
|
||||
// GET /enfants/:id ne joint pas toujours parent.user → noms manquants.
|
||||
return enrichEnfantParentNames(enfant);
|
||||
}
|
||||
|
||||
/// Complète [parentName] via GET parent quand l'API n'a pas fourni le nested user.
|
||||
static Future<EnfantAdminModel> enrichEnfantParentNames(
|
||||
EnfantAdminModel enfant,
|
||||
) async {
|
||||
final links = enfant.parentLinks;
|
||||
if (links.isEmpty) return enfant;
|
||||
if (links.every((l) => (l.parentName ?? '').trim().isNotEmpty)) {
|
||||
return enfant;
|
||||
}
|
||||
|
||||
final enriched = await Future.wait(
|
||||
links.map((link) async {
|
||||
if ((link.parentName ?? '').trim().isNotEmpty) return link;
|
||||
final id = link.parentId.trim();
|
||||
if (id.isEmpty) return link;
|
||||
try {
|
||||
final parent = await getParent(id);
|
||||
final name = parent.user.fullName.trim();
|
||||
if (name.isEmpty) return link;
|
||||
return link.copyWith(parentName: name);
|
||||
} catch (_) {
|
||||
return link;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return enfant.copyWith(parentLinks: enriched);
|
||||
}
|
||||
|
||||
static Future<EnfantAdminModel> updateEnfant({
|
||||
@ -475,7 +508,29 @@ class UserService {
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception(_extractErrorMessage(response.body, 'Erreur mise à jour enfant'));
|
||||
}
|
||||
return EnfantAdminModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
|
||||
final enfant = EnfantAdminModel.fromJson(
|
||||
jsonDecode(response.body) as Map<String, dynamic>,
|
||||
);
|
||||
return enrichEnfantParentNames(enfant);
|
||||
}
|
||||
|
||||
static Future<void> deleteEnfant(String enfantId) async {
|
||||
final response = await http.delete(
|
||||
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.enfants}/$enfantId'),
|
||||
headers: await _headers(),
|
||||
);
|
||||
if (response.statusCode != 200 && response.statusCode != 204) {
|
||||
throw Exception(_extractErrorMessage(response.body, 'Erreur suppression enfant'));
|
||||
}
|
||||
}
|
||||
|
||||
/// AM dont la liste d'enfants actifs contient [enfantId] (API actuelle).
|
||||
static Future<AssistanteMaternelleModel?> findAmForEnfant(String enfantId) async {
|
||||
final ams = await getAssistantesMaternelles();
|
||||
for (final am in ams) {
|
||||
if (am.children.any((c) => c.id == enfantId)) return am;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static ParentModel _parentModelFromBody(String body) {
|
||||
|
||||
@ -14,10 +14,10 @@ String normalizeEnfantStatus(String? raw) {
|
||||
return s;
|
||||
}
|
||||
|
||||
String _scolariseAccordeAuGenre(String? gender) {
|
||||
String _accordeAuGenre(String masculin, String feminin, String? gender) {
|
||||
final g = (gender ?? '').trim().toUpperCase();
|
||||
if (g == 'F') return 'Scolarisée';
|
||||
return 'Scolarisé';
|
||||
if (g == 'F') return feminin;
|
||||
return masculin;
|
||||
}
|
||||
|
||||
/// Libellé affiché pour un statut enfant.
|
||||
@ -28,9 +28,9 @@ String enfantStatusLabel(String? status, {String? gender}) {
|
||||
case 'sans_garde':
|
||||
return 'Sans garde';
|
||||
case 'garde':
|
||||
return 'En garde';
|
||||
return _accordeAuGenre('Gardé', 'Gardée', gender);
|
||||
case 'scolarise':
|
||||
return _scolariseAccordeAuGenre(gender);
|
||||
return _accordeAuGenre('Scolarisé', 'Scolarisée', gender);
|
||||
default:
|
||||
return status?.trim().isNotEmpty == true ? status!.trim() : '–';
|
||||
}
|
||||
|
||||
@ -167,6 +167,7 @@ class ParentRegistrationPayload {
|
||||
final map = <String, dynamic>{
|
||||
'genre': apiGenres.contains(c.genre) ? c.genre : 'Autre',
|
||||
'grossesse_multiple': c.multipleBirth,
|
||||
'consent_photo': c.photoConsent,
|
||||
};
|
||||
|
||||
final prenom = c.firstName.trim();
|
||||
|
||||
@ -38,15 +38,12 @@ class RepriseMapper {
|
||||
? isoToDdMmYyyy(e.dueDate)
|
||||
: isoToDdMmYyyy(e.birthDate);
|
||||
final photo = e.photoUrl?.trim();
|
||||
final hasPhoto = photo != null && photo.isNotEmpty;
|
||||
return ChildData(
|
||||
firstName: e.firstName ?? '',
|
||||
lastName: e.lastName ?? '',
|
||||
dob: dob,
|
||||
genre: e.gender ?? '',
|
||||
// Inscription initiale exigeait la coche pour envoyer la photo ; le back
|
||||
// ne persistait pas toujours consent_photo — on pré-coche si photo en base.
|
||||
photoConsent: e.consentPhoto || hasPhoto,
|
||||
photoConsent: e.consentPhoto,
|
||||
multipleBirth: e.estMultiple,
|
||||
isUnbornChild: isUnborn,
|
||||
cardColor: _childCardColors[index % _childCardColors.length],
|
||||
@ -200,7 +197,7 @@ class RepriseMapper {
|
||||
postalCode: dossier.codePostal ?? '',
|
||||
city: dossier.ville ?? '',
|
||||
existingPhotoUrl: displayPhoto,
|
||||
consentementPhoto: dossier.consentementPhoto || hasPhoto,
|
||||
consentementPhoto: dossier.consentementPhoto,
|
||||
dateOfBirth: parseIsoDate(dossier.dateNaissance),
|
||||
birthCity: dossier.lieuNaissanceVille ?? '',
|
||||
birthCountry: dossier.lieuNaissancePays ?? '',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -64,6 +64,7 @@ class AdminChildrenAffiliationPanel extends StatelessWidget {
|
||||
final c = children[i];
|
||||
return AdminEnfantUserCard.fromSummary(
|
||||
c,
|
||||
onCardTap: () => onOpen(c),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.visibility_outlined),
|
||||
|
||||
@ -14,6 +14,7 @@ class AdminUserCard extends StatefulWidget {
|
||||
final Color? infoColor;
|
||||
final String? vigilanceTooltip;
|
||||
final VoidCallback? onCardTap;
|
||||
final EdgeInsetsGeometry? margin;
|
||||
|
||||
const AdminUserCard({
|
||||
super.key,
|
||||
@ -28,6 +29,7 @@ class AdminUserCard extends StatefulWidget {
|
||||
this.infoColor,
|
||||
this.vigilanceTooltip,
|
||||
this.onCardTap,
|
||||
this.margin,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -59,7 +61,7 @@ class _AdminUserCardState extends State<AdminUserCard> {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
hoverColor: const Color(0x149CC5C0),
|
||||
child: Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
margin: widget.margin ?? const EdgeInsets.only(bottom: 12),
|
||||
elevation: 0,
|
||||
color: widget.backgroundColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
|
||||
@ -215,7 +215,7 @@ class _UserManagementPanelState extends State<UserManagementPanel> {
|
||||
value: 'garde',
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text('En garde', style: TextStyle(fontSize: 12)),
|
||||
child: Text('Gardé', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
),
|
||||
DropdownMenuItem<String?>(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user