nao aparecer para outro utilizador

This commit is contained in:
2026-03-19 10:40:53 +00:00
parent 8adea3f7b6
commit 6c89b7ab8c
6 changed files with 74 additions and 54 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 564 KiB

BIN
assets/playmaker-logos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -4,48 +4,49 @@ import '../models/game_model.dart';
class GameController { class GameController {
final _supabase = Supabase.instance.client; 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<List<Game>> get gamesStream { Stream<List<Game>> get gamesStream {
return _supabase return _supabase
.from('games') // 1. Fica à escuta da tabela original (Garante o Tempo Real!) .from('games')
.stream(primaryKey: ['id']) .stream(primaryKey: ['id'])
.eq('user_id', myUserId) // 🔒 SEGURANÇA: Ouve apenas os jogos deste utilizador
.asyncMap((event) async { .asyncMap((event) async {
// 2. Sempre que a tabela 'games' mudar (novo jogo, alteração de resultado), // Lê diretamente da tabela "games" e já não da "games_with_logos"
// vamos buscar os dados já misturados com as imagens à nossa View. final data = await _supabase
final viewData = await _supabase .from('games')
.from('games_with_logos')
.select() .select()
.eq('user_id', myUserId) // 🔒 SEGURANÇA
.order('game_date', ascending: false); .order('game_date', ascending: false);
// 3. Convertemos para a nossa lista de objetos Game return data.map((json) => Game.fromMap(json)).toList();
return viewData.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
// =========================================================================
// =========================================================================
// 👇 LER JOGOS COM FILTROS DE EQUIPA E TEMPORADA (SEM ERROS DE QUERY)
// ========================================================================= // =========================================================================
Stream<List<Game>> getFilteredGames({required String teamFilter, required String seasonFilter}) { Stream<List<Game>> getFilteredGames({required String teamFilter, required String seasonFilter}) {
return _supabase return _supabase
.from('games') .from('games')
.stream(primaryKey: ['id']) .stream(primaryKey: ['id'])
.eq('user_id', myUserId) // 🔒 SEGURANÇA
.asyncMap((event) async { .asyncMap((event) async {
// 1. Começamos a query APENAS com o select (Sem o order ainda!) // 1. Começamos a query na tabela principal "games"
var query = _supabase.from('games_with_logos').select(); var query = _supabase.from('games').select().eq('user_id', myUserId); // 🔒 SEGURANÇA
// 2. Se a temporada não for "Todas", aplicamos o filtro AQUI // 2. Se a temporada não for "Todas", aplicamos o filtro AQUI
if (seasonFilter != 'Todas') { if (seasonFilter != 'Todas') {
query = query.eq('season', seasonFilter); query = query.eq('season', seasonFilter);
} }
// 3. Executamos a query e aplicamos o ORDER BY no final // 3. Executamos a query e ordenamos pela data
final viewData = await query.order('game_date', ascending: false); final data = await query.order('game_date', ascending: false);
List<Game> games = viewData.map((json) => Game.fromMap(json)).toList(); List<Game> games = data.map((json) => Game.fromMap(json)).toList();
// 4. Filtramos a equipa em memória // 4. Filtramos a equipa em memória
if (teamFilter != 'Todas') { if (teamFilter != 'Todas') {
@@ -55,21 +56,22 @@ class GameController {
return games; return games;
}); });
} }
// 2. CRIAR JOGO // 2. CRIAR JOGO
// Retorna o ID do jogo criado para podermos navegar para o placar
Future<String?> createGame(String myTeam, String opponent, String season) async { Future<String?> createGame(String myTeam, String opponent, String season) async {
try { try {
final response = await _supabase.from('games').insert({ final response = await _supabase.from('games').insert({
'user_id': myUserId, // 🔒 CARIMBA O JOGO COM O ID DO TREINADOR
'my_team': myTeam, 'my_team': myTeam,
'opponent_team': opponent, 'opponent_team': opponent,
'season': season, 'season': season,
'my_score': 0, 'my_score': 0,
'opponent_score': 0, 'opponent_score': 0,
'status': 'Decorrer', // Começa como "Decorrer" 'status': 'Decorrer',
'game_date': DateTime.now().toIso8601String(), '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) { } catch (e) {
print("Erro ao criar jogo: $e"); print("Erro ao criar jogo: $e");
return null; return null;

View File

@@ -11,32 +11,49 @@ class BasketTrackHeader extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
SizedBox( // Usamos um Stack para controlar a sobreposição exata
width: 200 * context.sf, Stack(
height: 200 * context.sf, alignment: Alignment.center,
child: Image.asset( children: [
'assets/playmaker-logos.png', // 1. A Imagem (Aumentada para 320)
fit: BoxFit.contain, SizedBox(
), width: 320 * context.sf,
), height: 350 * context.sf,
Text( child: Image.asset(
'BasketTrack', 'assets/playmaker-logos.png',
style: TextStyle( fit: BoxFit.contain,
fontSize: 36 * context.sf, ),
fontWeight: FontWeight.bold, ),
color: Theme.of(context).colorScheme.onSurface, // 👇 Adaptável ao Modo Escuro // 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
SizedBox(height: 6 * context.sf), child: Column(
Text( children: [
'Gere as tuas equipas e estatísticas', Text(
style: TextStyle( 'BasketTrack',
fontSize: 16 * context.sf, style: TextStyle(
color: Colors.grey, // Mantemos cinza para subtítulo fontSize: 36 * context.sf,
fontWeight: FontWeight.w500, fontWeight: FontWeight.bold,
), color: Theme.of(context).colorScheme.onSurface,
textAlign: TextAlign.center, ),
),
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),
], ],
); );
} }

View File

@@ -61,10 +61,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.4.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@@ -268,18 +268,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.18" version: "0.12.17"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.0" version: "0.11.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@@ -553,10 +553,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.9" version: "0.7.7"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View File

@@ -58,6 +58,7 @@ flutter:
assets: assets:
- assets/playmaker-logo.png - assets/playmaker-logo.png
- assets/campo.png - assets/campo.png
- assets/playmaker-logos.png
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images # https://flutter.dev/to/resolution-aware-images