From 0befd4eb2a5cd19b3ad7bfc20a5f37af80af060f Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Mon, 13 Apr 2026 17:22:08 +0200 Subject: [PATCH] =?UTF-8?q?feat(admin):=20validation=20AM/famille,=20?= =?UTF-8?q?=C3=80=20valider=20au=20survol,=20nettoyage=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - À valider : ligne avec survol type AdminUserCard, icône Ouvrir centrée et plus grande (iconSize 34). - Wizard AM : titres Identité et coordonnées / Dossier professionnel / Présentation ; photo à gauche, grille droite [2,2,2,2] (NIR, naissance, agrément, capacité) ; AppUser champs naissance ; dates dd/MM/yyyy via formatIsoDateFr ; ValidationDetailSection titre optionnel. - Wizard famille : titre étape 4 « Présentation ». - Suppression des debugPrint liés médias / images / dossier (api_config, auth_network_image, dossier_unifie, user_service, validation_family). Made-with: Cursor --- frontend/lib/models/dossier_unifie.dart | 9 - frontend/lib/models/user.dart | 24 +++ frontend/lib/services/api/api_config.dart | 15 +- frontend/lib/services/user_service.dart | 17 -- frontend/lib/utils/date_display_utils.dart | 11 ++ .../common/validation_detail_section.dart | 24 ++- .../admin/pending_validation_widget.dart | 180 +++++++++++------- .../widgets/admin/validation_am_wizard.dart | 94 +++++---- .../admin/validation_family_wizard.dart | 22 +-- .../widgets/common/auth_network_image.dart | 15 -- 10 files changed, 212 insertions(+), 199 deletions(-) create mode 100644 frontend/lib/utils/date_display_utils.dart diff --git a/frontend/lib/models/dossier_unifie.dart b/frontend/lib/models/dossier_unifie.dart index 4ccd422..98cde26 100644 --- a/frontend/lib/models/dossier_unifie.dart +++ b/frontend/lib/models/dossier_unifie.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:p_tits_pas/models/user.dart'; /// Réponse unifiée GET /dossiers/:numeroDossier. Ticket #119, #107. @@ -213,14 +212,6 @@ class EnfantDossier { factory EnfantDossier.fromJson(Map json) { final rawPhoto = json['photo_url'] ?? json['photoUrl']; final resolvedPhoto = _optionalPhotoUrl(rawPhoto); - if (kDebugMode) { - debugPrint( - '[PetitsPas/dossier] EnfantDossier.fromJson id=${json['id']} ' - 'prénom=${json['first_name'] ?? json['prenom']} | ' - 'photo_url brute=$rawPhoto (${rawPhoto?.runtimeType}) → ' - 'résolu=${resolvedPhoto ?? "∅"}', - ); - } return EnfantDossier( id: json['id']?.toString() ?? '', firstName: (json['first_name'] ?? json['prenom'])?.toString(), diff --git a/frontend/lib/models/user.dart b/frontend/lib/models/user.dart index fc407d1..397e53a 100644 --- a/frontend/lib/models/user.dart +++ b/frontend/lib/models/user.dart @@ -16,6 +16,9 @@ class AppUser { final String? relaisId; final String? relaisNom; final String? numeroDossier; + final String? dateNaissance; + final String? lieuNaissanceVille; + final String? lieuNaissancePays; AppUser({ required this.id, @@ -35,6 +38,9 @@ class AppUser { this.relaisId, this.relaisNom, this.numeroDossier, + this.dateNaissance, + this.lieuNaissanceVille, + this.lieuNaissancePays, }); static String _str(dynamic v) { @@ -78,9 +84,24 @@ class AppUser { ?.toString(), relaisNom: relaisMap['nom']?.toString(), numeroDossier: json['numero_dossier'] is String ? json['numero_dossier'] as String : null, + dateNaissance: _optionalDateString(json['date_naissance']), + lieuNaissanceVille: json['lieu_naissance_ville'] is String + ? json['lieu_naissance_ville'] as String + : null, + lieuNaissancePays: json['lieu_naissance_pays'] is String + ? json['lieu_naissance_pays'] as String + : null, ); } + static String? _optionalDateString(dynamic v) { + if (v == null) return null; + if (v is String) return v.isEmpty ? null : v; + if (v is DateTime) return v.toIso8601String().split('T').first; + final s = v.toString(); + return s.isEmpty ? null : s; + } + Map toJson() { return { 'id': id, @@ -100,6 +121,9 @@ class AppUser { 'relais_id': relaisId, 'relais_nom': relaisNom, 'numero_dossier': numeroDossier, + 'date_naissance': dateNaissance, + 'lieu_naissance_ville': lieuNaissanceVille, + 'lieu_naissance_pays': lieuNaissancePays, }; } diff --git a/frontend/lib/services/api/api_config.dart b/frontend/lib/services/api/api_config.dart index 9d605dd..5e32ef4 100644 --- a/frontend/lib/services/api/api_config.dart +++ b/frontend/lib/services/api/api_config.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:p_tits_pas/config/env.dart'; class ApiConfig { @@ -21,26 +20,14 @@ class ApiConfig { /// On préfixe avec [baseUrl] (…/api/v1), pas seulement l’hôte : Traefik n’expose souvent que `/api`. static String absoluteMediaUrl(String? pathOrUrl) { if (pathOrUrl == null || pathOrUrl.trim().isEmpty) { - if (kDebugMode) { - debugPrint('[PetitsPas/media] absoluteMediaUrl: entrée vide (null ou "")'); - } return ''; } final u = pathOrUrl.trim(); if (u.startsWith('http://') || u.startsWith('https://')) { - if (kDebugMode) { - debugPrint('[PetitsPas/media] absoluteMediaUrl: déjà absolu → $u'); - } return u; } final base = baseUrl.replaceAll(RegExp(r'/+$'), ''); - final out = u.startsWith('/') ? '$base$u' : '$base/$u'; - if (kDebugMode) { - debugPrint( - '[PetitsPas/media] absoluteMediaUrl: base=$base | chemin brut="$u" → "$out"', - ); - } - return out; + return u.startsWith('/') ? '$base$u' : '$base/$u'; } // Auth endpoints diff --git a/frontend/lib/services/user_service.dart b/frontend/lib/services/user_service.dart index c0ab07e..425e4bf 100644 --- a/frontend/lib/services/user_service.dart +++ b/frontend/lib/services/user_service.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:p_tits_pas/models/user.dart'; import 'package:p_tits_pas/models/parent_model.dart'; @@ -94,9 +93,6 @@ class UserService { static Future getDossier(String numeroDossier) async { final encoded = Uri.encodeComponent(numeroDossier); final uri = Uri.parse('${ApiConfig.baseUrl}${ApiConfig.dossiers}/$encoded'); - if (kDebugMode) { - debugPrint('[PetitsPas/dossier] GET $uri'); - } final response = await http.get( uri, headers: await _headers(), @@ -119,19 +115,6 @@ class UserService { throw FormatException('Réponse invalide'); } final dossier = DossierUnifie.fromJson(Map.from(decoded)); - if (kDebugMode) { - debugPrint( - '[PetitsPas/dossier] réponse OK type=${dossier.type} | ' - 'ApiConfig.baseUrl=${ApiConfig.baseUrl} | apiOrigin=${ApiConfig.apiOrigin}', - ); - if (dossier.isFamily) { - final f = dossier.asFamily; - debugPrint( - '[PetitsPas/dossier] famille ${f.numeroDossier} | ' - '${f.enfants.length} enfant(s)', - ); - } - } return dossier; } catch (e) { if (e is FormatException) rethrow; diff --git a/frontend/lib/utils/date_display_utils.dart b/frontend/lib/utils/date_display_utils.dart new file mode 100644 index 0000000..075f2d2 --- /dev/null +++ b/frontend/lib/utils/date_display_utils.dart @@ -0,0 +1,11 @@ +import 'package:intl/intl.dart'; + +/// Affiche une date ISO / parseable en `dd/MM/yyyy`, avec repli sur la chaîne ou [ifEmpty]. +String formatIsoDateFr(String? s, {String ifEmpty = '–'}) { + if (s == null || s.trim().isEmpty) return ifEmpty; + try { + return DateFormat('dd/MM/yyyy').format(DateTime.parse(s.trim())); + } catch (_) { + return s.trim(); + } +} diff --git a/frontend/lib/widgets/admin/common/validation_detail_section.dart b/frontend/lib/widgets/admin/common/validation_detail_section.dart index deae30c..a0f36e2 100644 --- a/frontend/lib/widgets/admin/common/validation_detail_section.dart +++ b/frontend/lib/widgets/admin/common/validation_detail_section.dart @@ -5,7 +5,8 @@ import 'admin_detail_modal.dart'; /// [rowLayout] : même disposition que la création de compte, ex. [2, 2, 1, 2] = ligne de 2, ligne de 2, plein largeur, ligne de 2. /// [rowFlex] : flex par index de ligne (optionnel). Ex. {3: [2, 5]} = 4e ligne : code postal étroit (2), ville large (5). class ValidationDetailSection extends StatelessWidget { - final String title; + /// Si null ou vide, pas de bandeau titre (gain de place vertical, ex. wizard AM). + final String? title; final List fields; /// Nombre de champs par ligne (1 = plein largeur, 2 = deux côte à côte). Ex. [2, 2, 1, 2] pour identité. @@ -16,7 +17,7 @@ class ValidationDetailSection extends StatelessWidget { const ValidationDetailSection({ super.key, - required this.title, + this.title, required this.fields, this.rowLayout, this.rowFlex, @@ -60,19 +61,22 @@ class ValidationDetailSection extends StatelessWidget { )); } } + final showTitle = title != null && title!.trim().isNotEmpty; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ - Text( - title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.black87, + if (showTitle) ...[ + Text( + title!.trim(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), ), - ), - const SizedBox(height: 12), + const SizedBox(height: 12), + ], ...rows, ], ); diff --git a/frontend/lib/widgets/admin/pending_validation_widget.dart b/frontend/lib/widgets/admin/pending_validation_widget.dart index c1dac5b..e1e68a9 100644 --- a/frontend/lib/widgets/admin/pending_validation_widget.dart +++ b/frontend/lib/widgets/admin/pending_validation_widget.dart @@ -155,70 +155,6 @@ class _PendingValidationWidgetState extends State { ); } - /// Ligne commune : icône | titre (+ sous-titre) | bouton Ouvrir. - /// [titleWidget] remplace [title] si les deux sont fournis : priorité à [titleWidget]. - Widget _buildPendingRow({ - required IconData icon, - String? title, - Widget? titleWidget, - String? subtitle, - TextStyle? subtitleStyle, - required VoidCallback onOpen, - }) { - assert(title != null || titleWidget != null); - final titleChild = titleWidget ?? - Text( - title!, - style: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 14, - ), - ); - final subStyle = subtitleStyle ?? - TextStyle( - fontSize: 12, - color: Colors.grey.shade600, - ); - return Card( - margin: const EdgeInsets.only(bottom: 12), - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - side: BorderSide(color: Colors.grey.shade300), - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon(icon, color: Colors.grey.shade600, size: 28), - const SizedBox(width: 14), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - titleChild, - if (subtitle != null && subtitle.isNotEmpty) ...[ - const SizedBox(height: 4), - Text( - subtitle, - style: subStyle, - ), - ], - ], - ), - ), - ElevatedButton.icon( - onPressed: onOpen, - icon: const Icon(Icons.open_in_new, size: 18), - label: const Text('Ouvrir'), - ), - ], - ), - ), - ); - } /// Sous-titre AM : `email - date • tél. • CP ville` (plan affichage lignes À valider). String _amSubtitleLine(AppUser user) { @@ -244,9 +180,9 @@ class _PendingValidationWidgetState extends State { final numDossier = user.numeroDossier ?? '–'; final nameBold = user.fullName.isNotEmpty ? user.fullName : (user.email.isNotEmpty ? user.email : '–'); - return _buildPendingRow( + return _PendingValidationRow( icon: Icons.person_outline, - titleWidget: Text.rich( + title: Text.rich( TextSpan( style: const TextStyle(fontSize: 14, color: Colors.black87), children: [ @@ -320,9 +256,9 @@ class _PendingValidationWidgetState extends State { Widget _buildFamilyCard(PendingFamily family) { final numDossier = family.numeroDossier ?? '–'; final nameBold = family.libelle.isNotEmpty ? family.libelle : 'Famille'; - return _buildPendingRow( + return _PendingValidationRow( icon: Icons.family_restroom_outlined, - titleWidget: Text.rich( + title: Text.rich( TextSpan( style: const TextStyle(fontSize: 14, color: Colors.black87), children: [ @@ -352,3 +288,111 @@ class _PendingValidationWidgetState extends State { } } +/// Ligne « À valider » : survol comme [AdminUserCard], icône « Ouvrir » visible au hover uniquement. +class _PendingValidationRow extends StatefulWidget { + final IconData icon; + final Widget title; + final String? subtitle; + final TextStyle? subtitleStyle; + final VoidCallback onOpen; + + const _PendingValidationRow({ + required this.icon, + required this.title, + this.subtitle, + this.subtitleStyle, + required this.onOpen, + }); + + @override + State<_PendingValidationRow> createState() => _PendingValidationRowState(); +} + +class _PendingValidationRowState extends State<_PendingValidationRow> { + bool _isHovered = false; + + @override + Widget build(BuildContext context) { + final subStyle = widget.subtitleStyle ?? + TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + ); + return MouseRegion( + onEnter: (_) => setState(() => _isHovered = true), + onExit: (_) => setState(() => _isHovered = false), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(10), + child: InkWell( + onTap: () {}, + borderRadius: BorderRadius.circular(10), + hoverColor: const Color(0x149CC5C0), + child: Card( + margin: const EdgeInsets.only(bottom: 12), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide(color: Colors.grey.shade300), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(widget.icon, color: Colors.grey.shade600, size: 28), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + widget.title, + if (widget.subtitle != null && + widget.subtitle!.isNotEmpty) ...[ + const SizedBox(height: 4), + Text( + widget.subtitle!, + style: subStyle, + ), + ], + ], + ), + ), + SizedBox( + width: 52, + child: Center( + child: AnimatedOpacity( + duration: const Duration(milliseconds: 120), + opacity: _isHovered ? 1 : 0, + child: IgnorePointer( + ignoring: !_isHovered, + child: IconButtonTheme( + data: IconButtonThemeData( + style: IconButton.styleFrom( + padding: const EdgeInsets.all(0), + minimumSize: const Size(48, 48), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + ), + child: IconButton( + onPressed: widget.onOpen, + icon: const Icon(Icons.open_in_new), + iconSize: 34, + tooltip: 'Ouvrir', + ), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} + diff --git a/frontend/lib/widgets/admin/validation_am_wizard.dart b/frontend/lib/widgets/admin/validation_am_wizard.dart index 5a18332..d910db5 100644 --- a/frontend/lib/widgets/admin/validation_am_wizard.dart +++ b/frontend/lib/widgets/admin/validation_am_wizard.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/dossier_unifie.dart'; +import 'package:p_tits_pas/utils/date_display_utils.dart'; import 'package:p_tits_pas/utils/phone_utils.dart'; import 'package:p_tits_pas/utils/nir_utils.dart'; import 'package:p_tits_pas/models/user.dart'; @@ -74,29 +75,40 @@ class _ValidationAmWizardState extends State { AdminDetailField(label: 'Ville', value: _v(u.ville)), ]; - /// Informations professionnelles : N° Agrément|Date agrément, NIR, Capacité|Places, Ville. - List _proFields(DossierAM d) => [ - AdminDetailField(label: 'N° Agrément', value: _v(d.numeroAgrement)), - AdminDetailField( - label: 'Date d’agrément', - value: d.dateAgrement != null && d.dateAgrement!.trim().isNotEmpty - ? d.dateAgrement!.trim() - : '–', - ), - AdminDetailField(label: 'NIR', value: _formatNirForDisplay(d.nir)), - AdminDetailField( - label: 'Capacité max (enfants)', - value: d.nbMaxEnfants != null ? d.nbMaxEnfants.toString() : '–', - ), - AdminDetailField( - label: 'Places disponibles', - value: d.placesDisponibles != null - ? d.placesDisponibles.toString() - : '–', - ), - AdminDetailField( - label: 'Ville de résidence', value: _v(d.villeResidence)), - ]; + /// Panneau photo + grille droite : NIR|naissance, ville|pays, agrément|date, capa|places. + List _photoProFields(DossierAM d) { + final u = d.user; + return [ + AdminDetailField(label: 'NIR', value: _formatNirForDisplay(d.nir)), + AdminDetailField( + label: 'Date de naissance', + value: formatIsoDateFr(u.dateNaissance), + ), + AdminDetailField( + label: 'Ville de naissance', + value: _v(u.lieuNaissanceVille), + ), + AdminDetailField( + label: 'Pays de naissance', + value: _v(u.lieuNaissancePays), + ), + AdminDetailField(label: 'N° Agrément', value: _v(d.numeroAgrement)), + AdminDetailField( + label: 'Date d’agrément', + value: formatIsoDateFr(d.dateAgrement), + ), + AdminDetailField( + label: 'Capacité max (enfants)', + value: d.nbMaxEnfants != null ? d.nbMaxEnfants.toString() : '–', + ), + AdminDetailField( + label: 'Places disponibles', + value: d.placesDisponibles != null + ? d.placesDisponibles.toString() + : '–', + ), + ]; + } static const List _personalRowLayout = [2, 2, 1, 2]; static const Map> _personalRowFlex = { @@ -119,15 +131,6 @@ class _ValidationAmWizardState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Text( - 'Photo de profil', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - ), - const SizedBox(height: 12), Expanded( child: Padding( padding: const EdgeInsets.only(right: 8), @@ -264,7 +267,7 @@ class _ValidationAmWizardState extends State { child: ConstrainedBox( constraints: BoxConstraints(minWidth: constraints.maxWidth), child: ValidationDetailSection( - title: 'Informations personnelles', + title: 'Identité et coordonnées', fields: _personalFields(u), rowLayout: _personalRowLayout, rowFlex: _personalRowFlex, @@ -280,8 +283,7 @@ class _ValidationAmWizardState extends State { builder: (context, c) { final maxRowW = c.maxWidth; final maxRowH = c.maxHeight; - // Titre « Photo de profil » + espacement (~52 px) : hauteur dispo pour le cadre photo. - const photoHeaderH = 52.0; + const photoHeaderH = 0.0; final bodyH = (maxRowH - photoHeaderH).clamp(0.0, double.infinity); final idealPhotoW = bodyH * _idPhotoAspectRatio + 16; // marge approx. cadre clair @@ -307,14 +309,9 @@ class _ValidationAmWizardState extends State { constraints: BoxConstraints( minWidth: constraints.maxWidth), child: ValidationDetailSection( - title: 'Informations professionnelles', - fields: _proFields(d), - rowLayout: const [ - 2, - 1, - 2, - 1 - ], // N° Agrément|Date agrément, NIR, Capacité|Places, Ville + title: 'Dossier professionnel', + fields: _photoProFields(d), + rowLayout: const [2, 2, 2, 2], ), ), ); @@ -333,12 +330,13 @@ class _ValidationAmWizardState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Text( + const Text( 'Présentation', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.black87), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), ), const SizedBox(height: 12), Expanded( diff --git a/frontend/lib/widgets/admin/validation_family_wizard.dart b/frontend/lib/widgets/admin/validation_family_wizard.dart index 07bff2a..2178bf5 100644 --- a/frontend/lib/widgets/admin/validation_family_wizard.dart +++ b/frontend/lib/widgets/admin/validation_family_wizard.dart @@ -1,9 +1,8 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/gestures.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:intl/intl.dart'; import 'package:p_tits_pas/models/dossier_unifie.dart'; +import 'package:p_tits_pas/utils/date_display_utils.dart'; import 'package:p_tits_pas/utils/phone_utils.dart'; import 'package:p_tits_pas/services/user_service.dart'; import 'package:p_tits_pas/services/api/api_config.dart'; @@ -106,15 +105,8 @@ class _ValidationFamilyWizardState extends State { (s != null && s.trim().isNotEmpty) ? s.trim() : 'Non défini'; /// Date de naissance en jour/mois/année (dd/MM/yyyy). - static String _formatBirthDate(String? s) { - if (s == null || s.trim().isEmpty) return 'Non défini'; - try { - final d = DateTime.parse(s.trim()); - return DateFormat('dd/MM/yyyy').format(d); - } catch (_) { - return s.trim(); - } - } + static String _formatBirthDate(String? s) => + formatIsoDateFr(s, ifEmpty: 'Non défini'); /// Même ordre et disposition que le formulaire de création (Nom/Prénom, Tél/Email, Adresse, CP/Ville). List _parentFields(ParentDossier p) => [ @@ -344,12 +336,6 @@ class _ValidationFamilyWizardState extends State { /// Carte enfant : prénom pleine largeur, puis ligne photo 1/3 + colonne 2/3 (champs + statut hors TF si besoin). Widget _buildEnfantCard(EnfantDossier e) { final photoUrl = _fullPhotoUrl(e.photoUrl); - if (kDebugMode) { - debugPrint( - '[PetitsPas/validation-famille] carte enfant id=${e.id} prénom=${e.firstName} | ' - 'photoUrl modèle=${e.photoUrl ?? "∅"} | url affichée=${photoUrl.isEmpty ? "∅" : photoUrl}', - ); - } final columnStatusLabel = _enfantColumnStatusLabel(e); return ClipRRect( borderRadius: BorderRadius.circular(_enfantCardRadius), @@ -569,7 +555,7 @@ class _ValidationFamilyWizardState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( - 'Présentation / Motivation', + 'Présentation', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87), ), diff --git a/frontend/lib/widgets/common/auth_network_image.dart b/frontend/lib/widgets/common/auth_network_image.dart index 8562963..60ff0f2 100644 --- a/frontend/lib/widgets/common/auth_network_image.dart +++ b/frontend/lib/widgets/common/auth_network_image.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:p_tits_pas/services/api/tokenService.dart'; @@ -43,12 +42,6 @@ class _AuthNetworkImageState extends State { final isPublic = AuthNetworkImage.isPublicUploadUrl(widget.url); _headersFuture = isPublic ? Future?>.value(null) : _loadHeaders(); - if (kDebugMode) { - debugPrint( - '[PetitsPas/image] préparation chargement url=${widget.url} | ' - 'uploadPublic=$isPublic | avecBearer=${!isPublic}', - ); - } } static Future?> _loadHeaders() async { @@ -59,11 +52,6 @@ class _AuthNetworkImageState extends State { ImageErrorWidgetBuilder _wrapErrorBuilder() { return (BuildContext context, Object error, StackTrace? stackTrace) { - if (kDebugMode) { - debugPrint( - '[PetitsPas/image] ❌ échec chargement url=${widget.url} | erreur=$error', - ); - } final inner = widget.errorBuilder; if (inner != null) { return inner(context, error, stackTrace); @@ -112,9 +100,6 @@ class _AuthNetworkImageState extends State { ); } final headers = snapshot.data; - if (kDebugMode && headers != null) { - debugPrint('[PetitsPas/image] requête avec en-tête Authorization (Bearer présent)'); - } return Image.network( widget.url, width: widget.width,