/// Widget de carte d'organisation /// Respecte le design system établi avec les mêmes patterns que les autres cartes library organization_card; import 'package:flutter/material.dart'; import '../../data/models/organization_model.dart'; import '../../../../shared/design_system/tokens/module_colors.dart'; import '../../../../shared/design_system/tokens/app_colors.dart'; /// Carte d'organisation avec design cohérent — theme-aware (mode jour/nuit) class OrganizationCard extends StatelessWidget { final OrganizationModel organization; final VoidCallback? onTap; final VoidCallback? onEdit; final VoidCallback? onDelete; final bool showActions; const OrganizationCard({ super.key, required this.organization, this.onTap, this.onEdit, this.onDelete, this.showActions = true, }); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Container( margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( color: scheme.surface, borderRadius: BorderRadius.circular(8), border: Border.all(color: scheme.outline.withOpacity(0.5)), boxShadow: [ BoxShadow( color: scheme.shadow.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(context), const SizedBox(height: 8), _buildContent(context), const SizedBox(height: 8), _buildFooter(context), ], ), ), ), ); } /// Header avec nom et statut Widget _buildHeader(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Row( children: [ Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: ModuleColors.organisations.withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), child: const Icon(Icons.business_outlined, size: 18, color: Color(0xFF6C5CE7)), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( organization.nom, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: scheme.onSurface, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (organization.nomCourt?.isNotEmpty == true) ...[ const SizedBox(height: 2), Text( organization.nomCourt!, style: TextStyle( fontSize: 12, color: scheme.onSurfaceVariant, ), ), ], ], ), ), _buildStatusBadge(), ], ); } /// Badge de statut Widget _buildStatusBadge() { final color = Color(int.parse(organization.statut.color.substring(1), radix: 16) + 0xFF000000); return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( organization.statut.displayName, style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: color, ), ), ); } /// Contenu principal Widget _buildContent(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.category_outlined, size: 14, color: scheme.onSurfaceVariant), const SizedBox(width: 6), Text( organization.typeOrganisation, style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), ), ], ), const SizedBox(height: 4), if (organization.ville?.isNotEmpty == true || organization.region?.isNotEmpty == true) Row( children: [ Icon(Icons.location_on_outlined, size: 14, color: scheme.onSurfaceVariant), const SizedBox(width: 6), Expanded( child: Text( _buildLocationText(), style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 4), if (organization.description?.isNotEmpty == true) ...[ Text( organization.description!, style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), ], ], ); } /// Footer avec statistiques et actions Widget _buildFooter(BuildContext context) { return Row( children: [ Expanded( child: Row( children: [ _buildStatItem( context: context, icon: Icons.people_outline, value: organization.nombreMembres.toString(), label: 'membres', ), const SizedBox(width: 16), if (organization.ancienneteAnnees > 0) _buildStatItem( context: context, icon: Icons.access_time, value: organization.ancienneteAnnees.toString(), label: 'ans', ), ], ), ), if (showActions) _buildActions(), ], ); } /// Item de statistique Widget _buildStatItem({ required BuildContext context, required IconData icon, required String value, required String label, }) { final scheme = Theme.of(context).colorScheme; return Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 14, color: ModuleColors.organisations), const SizedBox(width: 4), Text( '$value $label', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: scheme.onSurface, ), ), ], ); } /// Actions (éditer, supprimer) Widget _buildActions() { return Row( mainAxisSize: MainAxisSize.min, children: [ if (onEdit != null) IconButton( onPressed: onEdit, icon: const Icon(Icons.edit_outlined, size: 18, color: Color(0xFF6C5CE7)), padding: const EdgeInsets.all(4), constraints: const BoxConstraints(minWidth: 32, minHeight: 32), tooltip: 'Modifier', ), if (onDelete != null) IconButton( onPressed: onDelete, icon: const Icon(Icons.delete_outline, size: 18, color: AppColors.error), padding: const EdgeInsets.all(4), constraints: const BoxConstraints(minWidth: 32, minHeight: 32), tooltip: 'Supprimer', ), ], ); } /// Construit le texte de localisation String _buildLocationText() { final parts = []; if (organization.ville?.isNotEmpty == true) parts.add(organization.ville!); if (organization.region?.isNotEmpty == true) parts.add(organization.region!); if (organization.pays?.isNotEmpty == true) parts.add(organization.pays!); return parts.join(', '); } }