From d7742432f4b00d3b18554b4f29d31a2bbfcaffc9 Mon Sep 17 00:00:00 2001 From: 230404 <230404@epvc.pt> Date: Wed, 21 Jan 2026 10:41:14 +0000 Subject: [PATCH] corrrigi team_stats_page --- lib/controllers/stats_controller.dart | 74 ++++++++++++--------------- lib/models/person_model.dart | 2 +- lib/screens/team_stats_page.dart | 35 ++++++++----- 3 files changed, 57 insertions(+), 54 deletions(-) diff --git a/lib/controllers/stats_controller.dart b/lib/controllers/stats_controller.dart index 2f77208..502e6c8 100644 --- a/lib/controllers/stats_controller.dart +++ b/lib/controllers/stats_controller.dart @@ -5,71 +5,52 @@ import '../models/person_model.dart'; class StatsController { final SupabaseClient _supabase = Supabase.instance.client; - // --- 1. LER DADOS (STREAM) --- + // --- 1. LER MEMBROS (STREAM EM TEMPO REAL) --- Stream> getMembers(String teamId) { return _supabase - .from('members') + .from('members') // Nome da tua tabela no Supabase .stream(primaryKey: ['id']) .eq('team_id', teamId) - .order('name', ascending: true) // Ordena por nome + .order('name', ascending: true) .map((data) => data.map((json) => Person.fromMap(json)).toList()); } - // --- 2. AÇÕES DE BASE DE DADOS --- - - // Adicionar - Future _addPersonToSupabase(String teamId, String name, String type, String number) async { - await _supabase.from('members').insert({ - 'team_id': teamId, - 'name': name, - 'type': type, - 'number': number, - }); - } - - // Editar - Future _updatePersonInSupabase(String personId, String name, String type, String number) async { - await _supabase.from('members').update({ - 'name': name, - 'type': type, - 'number': number, - }).eq('id', personId); - } - - // Apagar + // --- 2. APAGAR --- Future deletePerson(String teamId, String personId) async { try { await _supabase.from('members').delete().eq('id', personId); } catch (e) { - debugPrint("Erro ao apagar: $e"); + debugPrint("Erro ao eliminar: $e"); } } - // --- 3. DIÁLOGOS (UI) --- + // --- 3. DIÁLOGOS DE ADICIONAR / EDITAR --- - // Mostrar Diálogo de Adicionar + // Abrir diálogo para criar novo void showAddPersonDialog(BuildContext context, String teamId) { - _showPersonDialog(context, teamId: teamId); + _showPersonForm(context, teamId: teamId); } - // Mostrar Diálogo de Editar + // Abrir diálogo para editar existente void showEditPersonDialog(BuildContext context, String teamId, Person person) { - _showPersonDialog(context, teamId: teamId, person: person); + _showPersonForm(context, teamId: teamId, person: person); } - // Função Genérica para o Diálogo (Serve para criar e editar) - void _showPersonDialog(BuildContext context, {required String teamId, Person? person}) { + // Lógica interna do formulário + void _showPersonForm(BuildContext context, {required String teamId, Person? person}) { final isEditing = person != null; + + // Controladores de texto final nameController = TextEditingController(text: person?.name ?? ''); final numberController = TextEditingController(text: person?.number ?? ''); - // Valor inicial do dropdown ('Jogador' por defeito) + // Variável para o Dropdown (Valor inicial) String selectedType = person?.type ?? 'Jogador'; showDialog( context: context, builder: (context) { - // Usamos StatefulBuilder para atualizar o Dropdown dentro do Dialog + // StatefulBuilder serve para atualizar o Dropdown DENTRO do diálogo return StatefulBuilder( builder: (context, setState) { return AlertDialog( @@ -85,7 +66,7 @@ class StatsController { ), const SizedBox(height: 10), - // Tipo (Jogador/Treinador) + // Tipo (Jogador vs Treinador) DropdownButtonFormField( value: selectedType, decoration: const InputDecoration(labelText: 'Função'), @@ -101,7 +82,7 @@ class StatsController { ), const SizedBox(height: 10), - // Número (Só aparece se for Jogador) + // Número (Só mostramos se for Jogador) if (selectedType == 'Jogador') TextField( controller: numberController, @@ -118,17 +99,28 @@ class StatsController { ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF00C853)), onPressed: () async { - if (nameController.text.isEmpty) return; + if (nameController.text.trim().isEmpty) return; final name = nameController.text.trim(); final number = numberController.text.trim(); if (isEditing) { - await _updatePersonInSupabase(person!.id, name, selectedType, number); + // ATUALIZAR + await _supabase.from('members').update({ + 'name': name, + 'type': selectedType, + 'number': number, + }).eq('id', person!.id); } else { - await _addPersonToSupabase(teamId, name, selectedType, number); + // CRIAR NOVO + await _supabase.from('members').insert({ + 'team_id': teamId, + 'name': name, + 'type': selectedType, + 'number': number, + }); } - + if (context.mounted) Navigator.pop(context); }, child: Text(isEditing ? 'Guardar' : 'Adicionar', style: const TextStyle(color: Colors.white)), diff --git a/lib/models/person_model.dart b/lib/models/person_model.dart index dd6bb0b..840bce7 100644 --- a/lib/models/person_model.dart +++ b/lib/models/person_model.dart @@ -13,7 +13,7 @@ class Person { required this.number, }); - // Converter do Supabase (Map) para o Objeto + // Converte o JSON do Supabase para o objeto Person factory Person.fromMap(Map map) { return Person( id: map['id'] ?? '', diff --git a/lib/screens/team_stats_page.dart b/lib/screens/team_stats_page.dart index 50222fe..910dbb1 100644 --- a/lib/screens/team_stats_page.dart +++ b/lib/screens/team_stats_page.dart @@ -6,6 +6,7 @@ import '../widgets/stats_widgets.dart'; class TeamStatsPage extends StatelessWidget { final Team team; + // Agora este controller já tem o método getMembers final StatsController _controller = StatsController(); TeamStatsPage({super.key, required this.team}); @@ -16,16 +17,26 @@ class TeamStatsPage extends StatelessWidget { backgroundColor: const Color(0xFFF5F7FA), body: Column( children: [ - StatsHeader(team: team), + // Certifica-te que tens o StatsHeader no ficheiro stats_widgets.dart + StatsHeader(team: team), Expanded( child: StreamBuilder>( + // AGORA ISTO VAI FUNCIONAR: stream: _controller.getMembers(team.id), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } + if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } + final members = snapshot.data ?? []; + if (members.isEmpty) { + return const Center(child: Text("Sem membros nesta equipa.")); + } + final coaches = members.where((m) => m.type == 'Treinador').toList(); final players = members.where((m) => m.type == 'Jogador').toList(); @@ -34,13 +45,17 @@ class TeamStatsPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SummaryCard(), + const SummaryCard(), // Confirma se tens este widget const SizedBox(height: 30), - _buildSectionTitle("Treinadores"), - ...coaches.map((c) => _buildPersonCard(context, c, isCoach: true)), - const SizedBox(height: 30), - _buildSectionTitle("Jogadores"), - ...players.map((p) => _buildPersonCard(context, p, isCoach: false)), + if (coaches.isNotEmpty) ...[ + _buildSectionTitle("Treinadores"), + ...coaches.map((c) => _buildPersonCard(context, c, isCoach: true)), + const SizedBox(height: 30), + ], + if (players.isNotEmpty) ...[ + _buildSectionTitle("Jogadores"), + ...players.map((p) => _buildPersonCard(context, p, isCoach: false)), + ], const SizedBox(height: 80), ], ), @@ -58,7 +73,6 @@ class TeamStatsPage extends StatelessWidget { ); } - // CORREÇÃO: Adicionado BuildContext context como argumento Widget _buildPersonCard(BuildContext context, Person person, {required bool isCoach}) { return Card( margin: const EdgeInsets.only(top: 12), @@ -125,7 +139,4 @@ class TeamStatsPage extends StatelessWidget { ], ); } -} - -class StatsController { -} \ No newline at end of file +} \ No newline at end of file