From 0029c5ab869b440c98a513ffe84e9d2218fddd73 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Fri, 24 Jul 2026 19:39:55 +0200 Subject: [PATCH] fix(#153): peaufiner cartes dossiers et recherche. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cartes neutres avec accent icône, photo AM si dispo, hint court et infobulle de critères sur la barre de recherche. Co-authored-by: Cursor --- frontend/lib/models/dossier_list_item.dart | 5 ++ .../widgets/admin/common/admin_user_card.dart | 10 +++- .../lib/widgets/admin/dashboard_admin.dart | 57 +++++++++++++------ .../lib/widgets/admin/dossier_list_card.dart | 27 ++++----- .../admin/dossiers_management_widget.dart | 1 + .../admin/pending_validation_widget.dart | 1 + .../widgets/admin/user_management_panel.dart | 8 ++- 7 files changed, 77 insertions(+), 32 deletions(-) diff --git a/frontend/lib/models/dossier_list_item.dart b/frontend/lib/models/dossier_list_item.dart index 375c817..25ec22f 100644 --- a/frontend/lib/models/dossier_list_item.dart +++ b/frontend/lib/models/dossier_list_item.dart @@ -11,6 +11,8 @@ class DossierListItem { final String libelle; final List emails; final String? statut; + /// Photo profil (AM) — affichée à la place de l’icône si présente. + final String? photoUrl; const DossierListItem({ required this.type, @@ -18,6 +20,7 @@ class DossierListItem { required this.libelle, this.emails = const [], this.statut, + this.photoUrl, }); bool get isFamille => type == DossierListType.famille; @@ -125,12 +128,14 @@ class DossierListItem { prenom: u.prenom, email: u.email, ); + final photo = (u.photoUrl ?? '').trim(); return DossierListItem( type: DossierListType.assistanteMaternelle, numeroDossier: e.key, libelle: name.isNotEmpty ? name : 'AM', emails: u.email.trim().isEmpty ? const [] : [u.email.trim()], statut: u.statut?.trim(), + photoUrl: photo.isEmpty ? null : photo, ); }).toList(); } diff --git a/frontend/lib/widgets/admin/common/admin_user_card.dart b/frontend/lib/widgets/admin/common/admin_user_card.dart index f7cbc1b..640d293 100644 --- a/frontend/lib/widgets/admin/common/admin_user_card.dart +++ b/frontend/lib/widgets/admin/common/admin_user_card.dart @@ -12,6 +12,10 @@ class AdminUserCard extends StatefulWidget { final Color? backgroundColor; final Color? titleColor; final Color? infoColor; + /// Fond du cercle avatar / icône (défaut lavande admin). + final Color? avatarBackgroundColor; + /// Couleur de l’icône fallback (défaut violet admin). + final Color? avatarIconColor; final String? vigilanceTooltip; final VoidCallback? onCardTap; final EdgeInsetsGeometry? margin; @@ -28,6 +32,8 @@ class AdminUserCard extends StatefulWidget { this.backgroundColor, this.titleColor, this.infoColor, + this.avatarBackgroundColor, + this.avatarIconColor, this.vigilanceTooltip, this.onCardTap, this.margin, @@ -159,8 +165,8 @@ class _AdminUserCardState extends State { Widget _buildAvatar(String url) { const size = 28.0; - const bg = Color(0xFFEDE5FA); - const iconColor = Color(0xFF6B3FA0); + final bg = widget.avatarBackgroundColor ?? const Color(0xFFEDE5FA); + final iconColor = widget.avatarIconColor ?? const Color(0xFF6B3FA0); if (url.isEmpty) { return CircleAvatar( diff --git a/frontend/lib/widgets/admin/dashboard_admin.dart b/frontend/lib/widgets/admin/dashboard_admin.dart index 47c0983..b0cc6fa 100644 --- a/frontend/lib/widgets/admin/dashboard_admin.dart +++ b/frontend/lib/widgets/admin/dashboard_admin.dart @@ -8,6 +8,8 @@ class DashboardUserManagementSubBar extends StatelessWidget { final ValueChanged onSubTabChange; final TextEditingController searchController; final String searchHint; + /// Infobulle au survol de la barre de recherche (ex. critères de recherche). + final String? searchTooltip; final Widget? filterControl; final VoidCallback? onAddPressed; final String addLabel; @@ -22,12 +24,16 @@ class DashboardUserManagementSubBar extends StatelessWidget { 'Administrateurs', ]; + /// Aligné sur la taille des libellés d’onglets. + static const double _searchFontSize = 13; + const DashboardUserManagementSubBar({ Key? key, required this.selectedSubIndex, required this.onSubTabChange, required this.searchController, required this.searchHint, + this.searchTooltip, this.filterControl, this.onAddPressed, this.addLabel = '+ Ajouter', @@ -54,22 +60,7 @@ class DashboardUserManagementSubBar extends StatelessWidget { _buildSubNavItem(context, labels[i], i), ], const SizedBox(width: 36), - _pillField( - width: 320, - child: TextField( - controller: searchController, - decoration: InputDecoration( - hintText: searchHint, - prefixIcon: const Icon(Icons.search, size: 18), - border: InputBorder.none, - isDense: true, - contentPadding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 8, - ), - ), - ), - ), + _buildSearchField(), if (filterControl != null) ...[ const SizedBox(width: 12), _pillField(width: 150, child: filterControl!), @@ -81,6 +72,40 @@ class DashboardUserManagementSubBar extends StatelessWidget { ); } + Widget _buildSearchField() { + final field = _pillField( + width: 320, + child: TextField( + controller: searchController, + style: const TextStyle(fontSize: _searchFontSize), + decoration: InputDecoration( + hintText: searchHint, + hintStyle: TextStyle( + fontSize: _searchFontSize, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + color: Colors.black45, + ), + prefixIcon: const Icon(Icons.search, size: 18), + border: InputBorder.none, + isDense: true, + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + ), + ), + ); + final tip = (searchTooltip ?? '').trim(); + if (tip.isEmpty) return field; + return Tooltip( + message: tip, + preferBelow: false, + waitDuration: const Duration(milliseconds: 400), + child: field, + ); + } + Widget _pillField({required double width, required Widget child}) { return Container( width: width, diff --git a/frontend/lib/widgets/admin/dossier_list_card.dart b/frontend/lib/widgets/admin/dossier_list_card.dart index d0e3f67..b824eae 100644 --- a/frontend/lib/widgets/admin/dossier_list_card.dart +++ b/frontend/lib/widgets/admin/dossier_list_card.dart @@ -1,19 +1,20 @@ import 'package:flutter/material.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_user_card.dart'; -/// Carte dossier unifiée (#153) — pending et liste complète. +/// Carte dossier unifiée (#153) — fond neutre, accent couleur sur l’icône. class DossierListCard extends StatelessWidget { final String numeroDossier; final String namesLine; final bool isFamille; final VoidCallback onOpen; + /// Photo AM (si absente → icône fallback). + final String? photoUrl; - static const Color familleBg = Color(0xFFF3EEF8); - static const Color familleBorder = Color(0xFFB39DDB); - static const Color familleAccent = Color(0xFF7E57C2); - static const Color amBg = Color(0xFFE8F5F3); - static const Color amBorder = Color(0xFF80CBC4); - static const Color amAccent = Color(0xFF00897B); + /// Lavande — Famille / Parents. + static const Color familleAccent = Color(0xFFB289C9); + + /// Menthe logo — AM. + static const Color amAccent = Color(0xFF5A9D94); const DossierListCard({ super.key, @@ -21,24 +22,24 @@ class DossierListCard extends StatelessWidget { required this.namesLine, required this.isFamille, required this.onOpen, + this.photoUrl, }); @override Widget build(BuildContext context) { - final bg = isFamille ? familleBg : amBg; - final border = isFamille ? familleBorder : amBorder; final accent = isFamille ? familleAccent : amAccent; final num = numeroDossier.trim().isEmpty ? '–' : numeroDossier.trim(); final names = namesLine.trim(); + final avatar = (photoUrl ?? '').trim(); return AdminUserCard( title: num, subtitleLines: names.isEmpty ? const [] : [names], + avatarUrl: !isFamille && avatar.isNotEmpty ? avatar : null, fallbackIcon: - isFamille ? Icons.family_restroom_outlined : Icons.person_outline, - backgroundColor: bg, - borderColor: border, - titleColor: accent, + isFamille ? Icons.family_restroom_outlined : Icons.face, + // N° = titre neutre (comme Parents / AM) ; accent = icône seule. + avatarIconColor: accent, infoColor: Colors.black87, onCardTap: onOpen, actions: [ diff --git a/frontend/lib/widgets/admin/dossiers_management_widget.dart b/frontend/lib/widgets/admin/dossiers_management_widget.dart index 06f6113..62ec3ab 100644 --- a/frontend/lib/widgets/admin/dossiers_management_widget.dart +++ b/frontend/lib/widgets/admin/dossiers_management_widget.dart @@ -187,6 +187,7 @@ class _DossiersManagementWidgetState extends State { numeroDossier: item.numeroDossier, namesLine: item.namesLine, isFamille: item.isFamille, + photoUrl: item.photoUrl, onOpen: () => _openDossier(item.numeroDossier), ); }, diff --git a/frontend/lib/widgets/admin/pending_validation_widget.dart b/frontend/lib/widgets/admin/pending_validation_widget.dart index db30a71..78296bd 100644 --- a/frontend/lib/widgets/admin/pending_validation_widget.dart +++ b/frontend/lib/widgets/admin/pending_validation_widget.dart @@ -290,6 +290,7 @@ class _PendingValidationWidgetState extends State { numeroDossier: user.numeroDossier ?? '', namesLine: _amNamesLine(user), isFamille: false, + photoUrl: user.photoUrl, onOpen: () => _onOpenValidation(numeroDossier: user.numeroDossier), ); } diff --git a/frontend/lib/widgets/admin/user_management_panel.dart b/frontend/lib/widgets/admin/user_management_panel.dart index 6f4cb82..32e6017 100644 --- a/frontend/lib/widgets/admin/user_management_panel.dart +++ b/frontend/lib/widgets/admin/user_management_panel.dart @@ -89,7 +89,7 @@ class _UserManagementPanelState extends State { String _searchHintForTab() { switch (_subIndex) { case 0: - return 'Rechercher un dossier (n°, nom, email)…'; + return 'Rechercher un dossier'; case 1: return 'Rechercher un parent...'; case 2: @@ -105,6 +105,11 @@ class _UserManagementPanelState extends State { } } + String? _searchTooltipForTab() { + if (_subIndex != 0) return null; + return 'Recherche possible : n° de dossier, nom, prénom ou e-mail.'; + } + Widget? _subBarFilterControl() { // Parents if (_subIndex == 1) { @@ -285,6 +290,7 @@ class _UserManagementPanelState extends State { onSubTabChange: _onSubTabChange, searchController: _searchController, searchHint: _searchHintForTab(), + searchTooltip: _searchTooltipForTab(), filterControl: _subBarFilterControl(), // Pas de « Créer » sur l’onglet Dossiers (#153). onAddPressed: _isDossiersTab ? null : _handleAddPressed,