diff --git a/assets/playmaker-logo.png b/assets/playmaker-logo.png index d84412a..ec279ed 100644 Binary files a/assets/playmaker-logo.png and b/assets/playmaker-logo.png differ diff --git a/assets/playmaker-logos.png b/assets/playmaker-logos.png new file mode 100644 index 0000000..86e1cf9 Binary files /dev/null and b/assets/playmaker-logos.png differ diff --git a/lib/controllers/game_controller.dart b/lib/controllers/game_controller.dart index c0e5b6c..75eadcc 100644 --- a/lib/controllers/game_controller.dart +++ b/lib/controllers/game_controller.dart @@ -4,48 +4,49 @@ import '../models/game_model.dart'; class GameController { final _supabase = Supabase.instance.client; - // 1. LER JOGOS (Stream em Tempo Real) + // 👇 Atalho para apanhar o ID do utilizador logado + String get myUserId => _supabase.auth.currentUser?.id ?? ''; + + // 1. LER JOGOS (Stream em Tempo Real da tabela original) Stream> get gamesStream { return _supabase - .from('games') // 1. Fica à escuta da tabela original (Garante o Tempo Real!) + .from('games') .stream(primaryKey: ['id']) + .eq('user_id', myUserId) // 🔒 SEGURANÇA: Ouve apenas os jogos deste utilizador .asyncMap((event) async { - // 2. Sempre que a tabela 'games' mudar (novo jogo, alteração de resultado), - // vamos buscar os dados já misturados com as imagens à nossa View. - final viewData = await _supabase - .from('games_with_logos') + // Lê diretamente da tabela "games" e já não da "games_with_logos" + final data = await _supabase + .from('games') .select() + .eq('user_id', myUserId) // 🔒 SEGURANÇA .order('game_date', ascending: false); - // 3. Convertemos para a nossa lista de objetos Game - return viewData.map((json) => Game.fromMap(json)).toList(); + return data.map((json) => Game.fromMap(json)).toList(); }); } // ========================================================================= - // 👇 NOVO: LER JOGOS COM FILTROS DE EQUIPA E TEMPORADA (MANTÉM OS LOGOS) - // ========================================================================= - // ========================================================================= - // 👇 LER JOGOS COM FILTROS DE EQUIPA E TEMPORADA (SEM ERROS DE QUERY) + // 👇 LER JOGOS COM FILTROS DE EQUIPA E TEMPORADA // ========================================================================= Stream> getFilteredGames({required String teamFilter, required String seasonFilter}) { return _supabase .from('games') .stream(primaryKey: ['id']) + .eq('user_id', myUserId) // 🔒 SEGURANÇA .asyncMap((event) async { - // 1. Começamos a query APENAS com o select (Sem o order ainda!) - var query = _supabase.from('games_with_logos').select(); + // 1. Começamos a query na tabela principal "games" + var query = _supabase.from('games').select().eq('user_id', myUserId); // 🔒 SEGURANÇA // 2. Se a temporada não for "Todas", aplicamos o filtro AQUI if (seasonFilter != 'Todas') { query = query.eq('season', seasonFilter); } - // 3. Executamos a query e aplicamos o ORDER BY no final - final viewData = await query.order('game_date', ascending: false); + // 3. Executamos a query e ordenamos pela data + final data = await query.order('game_date', ascending: false); - List games = viewData.map((json) => Game.fromMap(json)).toList(); + List games = data.map((json) => Game.fromMap(json)).toList(); // 4. Filtramos a equipa em memória if (teamFilter != 'Todas') { @@ -55,21 +56,22 @@ class GameController { return games; }); } + // 2. CRIAR JOGO - // Retorna o ID do jogo criado para podermos navegar para o placar Future createGame(String myTeam, String opponent, String season) async { try { final response = await _supabase.from('games').insert({ + 'user_id': myUserId, // 🔒 CARIMBA O JOGO COM O ID DO TREINADOR 'my_team': myTeam, 'opponent_team': opponent, 'season': season, 'my_score': 0, 'opponent_score': 0, - 'status': 'Decorrer', // Começa como "Decorrer" + 'status': 'Decorrer', 'game_date': DateTime.now().toIso8601String(), - }).select().single(); // .select().single() retorna o objeto criado + }).select().single(); - return response['id']; // Retorna o UUID gerado pelo Supabase + return response['id']; } catch (e) { print("Erro ao criar jogo: $e"); return null; diff --git a/lib/widgets/login_widgets.dart b/lib/widgets/login_widgets.dart index 0736120..668cd60 100644 --- a/lib/widgets/login_widgets.dart +++ b/lib/widgets/login_widgets.dart @@ -11,32 +11,49 @@ class BasketTrackHeader extends StatelessWidget { Widget build(BuildContext context) { return Column( children: [ - SizedBox( - width: 200 * context.sf, - height: 200 * context.sf, - child: Image.asset( - 'assets/playmaker-logos.png', - fit: BoxFit.contain, - ), - ), - Text( - 'BasketTrack', - style: TextStyle( - fontSize: 36 * context.sf, - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onSurface, // 👇 Adaptável ao Modo Escuro - ), - ), - SizedBox(height: 6 * context.sf), - Text( - 'Gere as tuas equipas e estatísticas', - style: TextStyle( - fontSize: 16 * context.sf, - color: Colors.grey, // Mantemos cinza para subtítulo - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.center, + // Usamos um Stack para controlar a sobreposição exata + Stack( + alignment: Alignment.center, + children: [ + // 1. A Imagem (Aumentada para 320) + SizedBox( + width: 320 * context.sf, + height: 350 * context.sf, + child: Image.asset( + 'assets/playmaker-logos.png', + fit: BoxFit.contain, + ), + ), + // 2. O Texto "subido" para dentro da área da imagem + Positioned( + bottom: 5 * context.sf, // Ajusta este valor para aproximar/afastar do centro da logo + child: Column( + children: [ + Text( + 'BasketTrack', + style: TextStyle( + fontSize: 36 * context.sf, + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + SizedBox(height: 4 * context.sf), + Text( + 'Gere as tuas equipas e estatísticas', + style: TextStyle( + fontSize: 16 * context.sf, + color: Colors.grey, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ], ), + // Espaço extra para não bater nos campos de login logo a seguir + SizedBox(height: 10 * context.sf), ], ); } diff --git a/pubspec.lock b/pubspec.lock index bf5456b..720ba58 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" clock: dependency: transitive description: @@ -268,18 +268,18 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.11.1" meta: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: transitive description: name: test_api - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.9" + version: "0.7.7" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 036a8bf..590c58f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,7 @@ flutter: assets: - assets/playmaker-logo.png - assets/campo.png + - assets/playmaker-logos.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images