/// 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'; /// Carte d'organisation avec design cohérent 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) { return Container( margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), // RadiusTokens cohérent boxShadow: [ BoxShadow( color: Colors.black.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), // SpacingTokens cohérent child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), const SizedBox(height: 8), _buildContent(), const SizedBox(height: 8), _buildFooter(), ], ), ), ), ); } /// Header avec nom et statut Widget _buildHeader() { return Row( children: [ // Icône du type d'organisation Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: const Color(0xFF6C5CE7).withOpacity(0.1), // ColorTokens cohérent borderRadius: BorderRadius.circular(6), ), child: Text( organization.typeOrganisation.icon, style: const TextStyle(fontSize: 16), ), ), const SizedBox(width: 12), // Nom et nom court Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( organization.nom, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF374151), // ColorTokens cohérent ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (organization.nomCourt?.isNotEmpty == true) ...[ const SizedBox(height: 2), Text( organization.nomCourt!, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), ), ), ], ], ), ), // Badge de statut _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() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Type d'organisation Row( children: [ const Icon( Icons.category_outlined, size: 14, color: Color(0xFF6B7280), ), const SizedBox(width: 6), Text( organization.typeOrganisation.displayName, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), ), ), ], ), const SizedBox(height: 4), // Localisation if (organization.ville?.isNotEmpty == true || organization.region?.isNotEmpty == true) Row( children: [ const Icon( Icons.location_on_outlined, size: 14, color: Color(0xFF6B7280), ), const SizedBox(width: 6), Expanded( child: Text( _buildLocationText(), style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 4), // Description si disponible if (organization.description?.isNotEmpty == true) ...[ Text( organization.description!, style: const TextStyle( fontSize: 12, color: Color(0xFF6B7280), ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), ], ], ); } /// Footer avec statistiques et actions Widget _buildFooter() { return Row( children: [ // Statistiques Expanded( child: Row( children: [ _buildStatItem( icon: Icons.people_outline, value: organization.nombreMembres.toString(), label: 'membres', ), const SizedBox(width: 16), if (organization.ancienneteAnnees > 0) _buildStatItem( icon: Icons.access_time, value: organization.ancienneteAnnees.toString(), label: 'ans', ), ], ), ), // Actions if (showActions) _buildActions(), ], ); } /// Item de statistique Widget _buildStatItem({ required IconData icon, required String value, required String label, }) { return Row( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 14, color: const Color(0xFF6C5CE7), ), const SizedBox(width: 4), Text( '$value $label', style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xFF374151), ), ), ], ); } /// 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: Icon( Icons.delete_outline, size: 18, color: Colors.red.shade400, ), 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(', '); } }