import 'dart:async'; import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:playmaker/classe/theme.dart'; // ๐Ÿ‘‡ IMPORT DO TEMA! import '../models/team_model.dart'; import '../models/person_model.dart'; import '../utils/size_extension.dart'; // ๐Ÿ‘‡ SUPERPODER SF // --- CABEร‡ALHO --- class StatsHeader extends StatelessWidget { final Team team; const StatsHeader({super.key, required this.team}); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.only(top: 50 * context.sf, left: 20 * context.sf, right: 20 * context.sf, bottom: 20 * context.sf), decoration: BoxDecoration( color: AppTheme.primaryRed, // ๐Ÿ‘‡ Usando a cor oficial borderRadius: BorderRadius.only( bottomLeft: Radius.circular(30 * context.sf), bottomRight: Radius.circular(30 * context.sf) ), ), child: Row( children: [ IconButton( icon: Icon(Icons.arrow_back, color: Colors.white, size: 24 * context.sf), onPressed: () => Navigator.pop(context), ), SizedBox(width: 10 * context.sf), CircleAvatar( radius: 24 * context.sf, backgroundColor: Colors.white24, backgroundImage: (team.imageUrl.isNotEmpty && team.imageUrl.startsWith('http')) ? NetworkImage(team.imageUrl) : null, child: (team.imageUrl.isEmpty || !team.imageUrl.startsWith('http')) ? Text( team.imageUrl.isEmpty ? "๐Ÿ›ก๏ธ" : team.imageUrl, style: TextStyle(fontSize: 20 * context.sf), ) : null, ), SizedBox(width: 15 * context.sf), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( team.name, style: TextStyle(color: Colors.white, fontSize: 20 * context.sf, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis ), Text( team.season, style: TextStyle(color: Colors.white70, fontSize: 14 * context.sf) ), ], ), ), ], ), ); } } // --- CARD DE RESUMO --- class StatsSummaryCard extends StatelessWidget { final int total; const StatsSummaryCard({super.key, required this.total}); @override Widget build(BuildContext context) { // ๐Ÿ‘‡ Adapta-se ao Modo Claro/Escuro final Color bgColor = Theme.of(context).brightness == Brightness.dark ? const Color(0xFF1E1E1E) : Colors.white; return Card( elevation: 4, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20 * context.sf)), child: Container( padding: EdgeInsets.all(20 * context.sf), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(20 * context.sf), border: Border.all(color: Colors.grey.withOpacity(0.15)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.groups, color: AppTheme.primaryRed, size: 28 * context.sf), // ๐Ÿ‘‡ Cor do tema SizedBox(width: 10 * context.sf), Text( "Total de Membros", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 16 * context.sf, fontWeight: FontWeight.w600) ), ], ), Text( "$total", style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 28 * context.sf, fontWeight: FontWeight.bold) ), ], ), ), ); } } // --- TรTULO DE SECร‡รƒO --- class StatsSectionTitle extends StatelessWidget { final String title; const StatsSectionTitle({super.key, required this.title}); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle(fontSize: 18 * context.sf, fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onSurface) ), Divider(color: Colors.grey.withOpacity(0.2)), ], ); } } // --- CARD DA PESSOA (JOGADOR/TREINADOR) --- class PersonCard extends StatelessWidget { final Person person; final bool isCoach; final VoidCallback onEdit; final VoidCallback onDelete; const PersonCard({ super.key, required this.person, required this.isCoach, required this.onEdit, required this.onDelete, }); @override Widget build(BuildContext context) { // ๐Ÿ‘‡ Adapta as cores do Card ao Modo Escuro e ao Tema final Color defaultBg = Theme.of(context).brightness == Brightness.dark ? const Color(0xFF1E1E1E) : Colors.white; final Color coachBg = Theme.of(context).brightness == Brightness.dark ? AppTheme.warningAmber.withOpacity(0.1) : const Color(0xFFFFF9C4); return Card( margin: EdgeInsets.only(top: 12 * context.sf), elevation: 2, color: isCoach ? coachBg : defaultBg, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15 * context.sf)), child: ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 16 * context.sf, vertical: 4 * context.sf), leading: isCoach ? CircleAvatar( radius: 22 * context.sf, backgroundColor: AppTheme.warningAmber, // ๐Ÿ‘‡ Cor do tema child: Icon(Icons.person, color: Colors.white, size: 24 * context.sf) ) : Container( width: 45 * context.sf, height: 45 * context.sf, alignment: Alignment.center, decoration: BoxDecoration( color: AppTheme.primaryRed.withOpacity(0.1), // ๐Ÿ‘‡ Cor do tema borderRadius: BorderRadius.circular(10 * context.sf) ), child: Text( person.number ?? "J", style: TextStyle(color: AppTheme.primaryRed, fontWeight: FontWeight.bold, fontSize: 16 * context.sf) ), ), title: Text( person.name, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16 * context.sf, color: Theme.of(context).colorScheme.onSurface) ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon(Icons.edit_outlined, color: Colors.blue, size: 22 * context.sf), onPressed: onEdit, ), IconButton( icon: Icon(Icons.delete_outline, color: AppTheme.primaryRed, size: 22 * context.sf), // ๐Ÿ‘‡ Cor do tema onPressed: onDelete, ), ], ), ), ); } } // ========================================== // 2. PรGINA PRINCIPAL // ========================================== class TeamStatsPage extends StatefulWidget { final Team team; const TeamStatsPage({super.key, required this.team}); @override State createState() => _TeamStatsPageState(); } class _TeamStatsPageState extends State { final StatsController _controller = StatsController(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, // ๐Ÿ‘‡ Adapta-se ao Modo Escuro body: Column( children: [ StatsHeader(team: widget.team), Expanded( child: StreamBuilder>( stream: _controller.getMembers(widget.team.id), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator(color: AppTheme.primaryRed)); } if (snapshot.hasError) { return Center(child: Text("Erro ao carregar: ${snapshot.error}", style: TextStyle(color: Theme.of(context).colorScheme.onSurface))); } final members = snapshot.data ?? []; final coaches = members.where((m) => m.type == 'Treinador').toList(); final players = members.where((m) => m.type == 'Jogador').toList(); return RefreshIndicator( color: AppTheme.primaryRed, onRefresh: () async => setState(() {}), child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: EdgeInsets.all(16.0 * context.sf), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ StatsSummaryCard(total: members.length), SizedBox(height: 30 * context.sf), // TREINADORES if (coaches.isNotEmpty) ...[ const StatsSectionTitle(title: "Treinadores"), ...coaches.map((c) => PersonCard( person: c, isCoach: true, onEdit: () => _controller.showEditPersonDialog(context, widget.team.id, c), onDelete: () => _confirmDelete(context, c), )), SizedBox(height: 30 * context.sf), ], // JOGADORES const StatsSectionTitle(title: "Jogadores"), if (players.isEmpty) Padding( padding: EdgeInsets.only(top: 20 * context.sf), child: Text("Nenhum jogador nesta equipa.", style: TextStyle(color: Colors.grey, fontSize: 16 * context.sf)), ) else ...players.map((p) => PersonCard( person: p, isCoach: false, onEdit: () => _controller.showEditPersonDialog(context, widget.team.id, p), onDelete: () => _confirmDelete(context, p), )), SizedBox(height: 80 * context.sf), ], ), ), ); }, ), ), ], ), floatingActionButton: FloatingActionButton( heroTag: 'fab_team_${widget.team.id}', onPressed: () => _controller.showAddPersonDialog(context, widget.team.id), backgroundColor: AppTheme.successGreen, // ๐Ÿ‘‡ Cor de sucesso do tema child: Icon(Icons.add, color: Colors.white, size: 24 * context.sf), ), ); } void _confirmDelete(BuildContext context, Person person) { showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: Theme.of(context).colorScheme.surface, title: Text("Eliminar Membro?", style: TextStyle(color: Theme.of(context).colorScheme.onSurface)), content: Text("Tens a certeza que queres remover ${person.name}?", style: TextStyle(color: Theme.of(context).colorScheme.onSurface)), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text("Cancelar", style: TextStyle(color: Colors.grey)) ), TextButton( onPressed: () async { await _controller.deletePerson(person.id); if (ctx.mounted) Navigator.pop(ctx); }, child: Text("Eliminar", style: TextStyle(color: AppTheme.primaryRed)), // ๐Ÿ‘‡ Cor oficial ), ], ), ); } } // ========================================== // 3. CONTROLLER // ========================================== class StatsController { final _supabase = Supabase.instance.client; Stream> getMembers(String teamId) { return _supabase .from('members') .stream(primaryKey: ['id']) .eq('team_id', teamId) .order('name', ascending: true) .map((data) => data.map((json) => Person.fromMap(json)).toList()); } Future deletePerson(String personId) async { try { await _supabase.from('members').delete().eq('id', personId); } catch (e) { debugPrint("Erro ao eliminar: $e"); } } void showAddPersonDialog(BuildContext context, String teamId) { _showForm(context, teamId: teamId); } void showEditPersonDialog(BuildContext context, String teamId, Person person) { _showForm(context, teamId: teamId, person: person); } void _showForm(BuildContext context, {required String teamId, Person? person}) { final isEdit = person != null; final nameCtrl = TextEditingController(text: person?.name ?? ''); final numCtrl = TextEditingController(text: person?.number ?? ''); String selectedType = person?.type ?? 'Jogador'; showDialog( context: context, builder: (ctx) => StatefulBuilder( builder: (ctx, setState) => AlertDialog( backgroundColor: Theme.of(context).colorScheme.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15 * context.sf)), title: Text( isEdit ? "Editar Membro" : "Novo Membro", style: TextStyle(color: Theme.of(context).colorScheme.onSurface) ), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameCtrl, style: TextStyle(color: Theme.of(context).colorScheme.onSurface), decoration: const InputDecoration(labelText: "Nome Completo"), textCapitalization: TextCapitalization.words, ), SizedBox(height: 15 * context.sf), DropdownButtonFormField( value: selectedType, dropdownColor: Theme.of(context).colorScheme.surface, style: TextStyle(color: Theme.of(context).colorScheme.onSurface, fontSize: 16 * context.sf), decoration: const InputDecoration(labelText: "Funรงรฃo"), items: ["Jogador", "Treinador"] .map((e) => DropdownMenuItem(value: e, child: Text(e))) .toList(), onChanged: (v) { if (v != null) setState(() => selectedType = v); }, ), if (selectedType == "Jogador") ...[ SizedBox(height: 15 * context.sf), TextField( controller: numCtrl, style: TextStyle(color: Theme.of(context).colorScheme.onSurface), decoration: const InputDecoration(labelText: "Nรบmero da Camisola"), keyboardType: TextInputType.number, ), ] ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text("Cancelar", style: TextStyle(color: Colors.grey)) ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: AppTheme.successGreen, // ๐Ÿ‘‡ Cor verde do tema foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8 * context.sf)) ), onPressed: () async { if (nameCtrl.text.trim().isEmpty) return; String? numeroFinal = (selectedType == "Treinador") ? null : (numCtrl.text.trim().isEmpty ? null : numCtrl.text.trim()); try { if (isEdit) { await _supabase.from('members').update({ 'name': nameCtrl.text.trim(), 'type': selectedType, 'number': numeroFinal, }).eq('id', person.id); } else { await _supabase.from('members').insert({ 'team_id': teamId, 'name': nameCtrl.text.trim(), 'type': selectedType, 'number': numeroFinal, }); } if (ctx.mounted) Navigator.pop(ctx); } catch (e) { debugPrint("Erro Supabase: $e"); if (ctx.mounted) { String errorMsg = "Erro ao guardar: $e"; if (e.toString().contains('unique')) { errorMsg = "Jรก existe um membro com este numero na equipa."; } ScaffoldMessenger.of(ctx).showSnackBar( SnackBar(content: Text(errorMsg), backgroundColor: AppTheme.primaryRed) // ๐Ÿ‘‡ Cor oficial para erro ); } } }, child: const Text("Guardar"), ) ], ), ), ); } }