bora para o relatorio

This commit is contained in:
2026-05-21 10:51:35 +01:00
parent 332361c296
commit 7d2f3c4679
3 changed files with 435 additions and 293 deletions

View File

@@ -404,293 +404,336 @@ class _HomeScreenState extends State<HomeScreen> {
padding: EdgeInsets.symmetric(
horizontal: 22.0 * context.sf,
vertical: 16.0 * context.sf),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () => _showTeamSelector(context),
child: Container(
padding: EdgeInsets.all(12 * context.sf),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color,
borderRadius:
BorderRadius.circular(15 * context.sf),
border:
Border.all(color: Colors.grey.withOpacity(0.2)),
child: LayoutBuilder(
builder: (context, constraints) {
final bool isWide = constraints.maxWidth >= 1100;
final double effectiveCardHeight =
isWide ? 280 * context.sf : cardHeight;
Widget statsSection = Column(
children: [
SizedBox(
height: effectiveCardHeight,
child: Row(
children: [
Expanded(
child: _buildStatCard(
context: context,
title: 'Mais Pontos',
playerName: leaders['pts_name'],
statValue: leaders['pts_val'].toString(),
statLabel: 'TOTAL',
color: AppTheme.statPtsBg,
isHighlighted: true)),
SizedBox(width: 12 * context.sf),
Expanded(
child: _buildStatCard(
context: context,
title: 'Assistências',
playerName: leaders['ast_name'],
statValue: leaders['ast_val'].toString(),
statLabel: 'TOTAL',
color: AppTheme.statAstBg)),
],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
(_selectedTeamLogo != null &&
_selectedTeamLogo!.isNotEmpty)
? ClipOval(
child: CachedNetworkImage(
imageUrl: _selectedTeamLogo!,
width: 24 * context.sf,
height: 24 * context.sf,
fit: BoxFit.cover,
placeholder: (context, url) => Icon(
Icons.shield,
color: AppTheme.primaryRed,
size: 24 * context.sf),
errorWidget:
(context, url, error) => Icon(
SizedBox(height: 12 * context.sf),
SizedBox(
height: effectiveCardHeight,
child: Row(
children: [
Expanded(
child: _buildStatCard(
context: context,
title: 'Rebotes',
playerName: leaders['rbs_name'],
statValue: leaders['rbs_val'].toString(),
statLabel: 'TOTAL',
color: AppTheme.statRebBg)),
SizedBox(width: 12 * context.sf),
Expanded(
child: PieChartCard(
victories: _teamWins,
defeats: _teamLosses,
draws: _teamDraws,
title: 'DESEMPENHO',
subtitle: 'Temporada',
backgroundColor: AppTheme.statPieBg,
sf: context.sf)),
],
),
),
],
);
Widget historySection = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Histórico de Jogos',
style: TextStyle(
fontSize: 20 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
SizedBox(height: 16 * context.sf),
_selectedTeamName == "Selecionar Equipa"
? Container(
width: double.infinity,
padding: EdgeInsets.all(24.0 * context.sf),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color ??
Colors.white,
borderRadius:
BorderRadius.circular(16 * context.sf),
border: Border.all(
color: Colors.grey.withOpacity(0.1)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 10,
offset: const Offset(0, 4))
],
),
child: Column(
children: [
Container(
padding: EdgeInsets.all(18 * context.sf),
decoration: BoxDecoration(
color: AppTheme.primaryRed
.withOpacity(0.08),
shape: BoxShape.circle),
child: Icon(Icons.shield_outlined,
color: AppTheme.primaryRed,
size: 42 * context.sf),
),
SizedBox(height: 20 * context.sf),
Text("Nenhuma Equipa Ativa",
style: TextStyle(
fontSize: 18 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
SizedBox(height: 8 * context.sf),
Text(
"Escolha uma equipa no seletor acima para ver as estatísticas e o histórico.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13 * context.sf,
color: Colors.grey.shade600,
height: 1.4),
),
SizedBox(height: 24 * context.sf),
SizedBox(
width: double.infinity,
height: 48 * context.sf,
child: ElevatedButton.icon(
onPressed: () => _showTeamSelector(context),
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.primaryRed,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10 * context.sf)),
),
icon: Icon(Icons.touch_app,
size: 20 * context.sf),
label: Text("Selecionar Agora",
style: TextStyle(
fontSize: 15 * context.sf,
fontWeight: FontWeight.bold)),
),
),
],
),
)
: StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase
.from('games')
.stream(primaryKey: ['id'])
.order('game_date', ascending: false),
builder: (context, gameSnapshot) {
if (gameSnapshot.hasError) {
return Text(
"Erro: ${gameSnapshot.error}",
style: const TextStyle(
color: Colors.red));
}
if (!gameSnapshot.hasData &&
gameSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator());
}
final todosOsJogos = gameSnapshot.data ?? [];
final gamesList = todosOsJogos.where((game) {
String myT =
game['my_team']?.toString() ?? '';
String oppT =
game['opponent_team']?.toString() ?? '';
String status =
game['status']?.toString() ?? '';
return (myT == _selectedTeamName ||
oppT == _selectedTeamName) &&
status == 'Terminado';
}).take(3).toList();
if (gamesList.isEmpty) {
return Container(
width: double.infinity,
padding: EdgeInsets.all(20 * context.sf),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color,
borderRadius: BorderRadius.circular(14),
),
alignment: Alignment.center,
child: const Text(
"Ainda não há jogos terminados.",
style: TextStyle(color: Colors.grey)),
);
}
return Column(
children: gamesList.map((game) {
String dbMyTeam =
game['my_team']?.toString() ?? '';
String dbOppTeam =
game['opponent_team']?.toString() ?? '';
int dbMyScore = int.tryParse(
game['my_score']?.toString() ??
'0') ??
0;
int dbOppScore = int.tryParse(
game['opponent_score']
?.toString() ??
'0') ??
0;
String opponent;
int myScore;
int oppScore;
if (dbMyTeam == _selectedTeamName) {
opponent = dbOppTeam;
myScore = dbMyScore;
oppScore = dbOppScore;
} else {
opponent = dbMyTeam;
myScore = dbOppScore;
oppScore = dbMyScore;
}
String rawDate =
game['game_date']?.toString() ?? '---';
String date = rawDate.length >= 10
? rawDate.substring(0, 10)
: rawDate;
String result = myScore > oppScore
? 'V'
: (myScore < oppScore ? 'D' : 'E');
return _buildGameHistoryCard(
context: context,
opponent: opponent,
result: result,
myScore: myScore,
oppScore: oppScore,
date: date,
topPts:
game['top_pts_name'] ?? '---',
topAst:
game['top_ast_name'] ?? '---',
topRbs:
game['top_rbs_name'] ?? '---',
topDef:
game['top_def_name'] ?? '---',
mvp: game['mvp_name'] ?? '---',
);
}).toList(),
);
},
),
],
);
Widget mainContent = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () => _showTeamSelector(context),
child: Container(
padding: EdgeInsets.all(12 * context.sf),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color,
borderRadius:
BorderRadius.circular(15 * context.sf),
border:
Border.all(color: Colors.grey.withOpacity(0.2)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
(_selectedTeamLogo != null &&
_selectedTeamLogo!.isNotEmpty)
? ClipOval(
child: CachedNetworkImage(
imageUrl: _selectedTeamLogo!,
width: 24 * context.sf,
height: 24 * context.sf,
fit: BoxFit.cover,
placeholder: (context, url) => Icon(
Icons.shield,
color: AppTheme.primaryRed,
size: 24 * context.sf),
),
)
: Icon(Icons.shield,
color: AppTheme.primaryRed,
size: 24 * context.sf),
SizedBox(width: 10 * context.sf),
Text(_selectedTeamName,
style: TextStyle(
fontSize: 16 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
]),
Icon(Icons.arrow_drop_down, color: textColor),
],
),
),
),
SizedBox(height: 20 * context.sf),
SizedBox(
height: cardHeight,
child: Row(
children: [
Expanded(
child: _buildStatCard(
context: context,
title: 'Mais Pontos',
playerName: leaders['pts_name'],
statValue: leaders['pts_val'].toString(),
statLabel: 'TOTAL',
color: AppTheme.statPtsBg,
isHighlighted: true)),
SizedBox(width: 12 * context.sf),
Expanded(
child: _buildStatCard(
context: context,
title: 'Assistências',
playerName: leaders['ast_name'],
statValue: leaders['ast_val'].toString(),
statLabel: 'TOTAL',
color: AppTheme.statAstBg)),
],
),
),
SizedBox(height: 12 * context.sf),
SizedBox(
height: cardHeight,
child: Row(
children: [
Expanded(
child: _buildStatCard(
context: context,
title: 'Rebotes',
playerName: leaders['rbs_name'],
statValue: leaders['rbs_val'].toString(),
statLabel: 'TOTAL',
color: AppTheme.statRebBg)),
SizedBox(width: 12 * context.sf),
Expanded(
child: PieChartCard(
victories: _teamWins,
defeats: _teamLosses,
draws: _teamDraws,
title: 'DESEMPENHO',
subtitle: 'Temporada',
backgroundColor: AppTheme.statPieBg,
sf: context.sf)),
],
),
),
SizedBox(height: 40 * context.sf),
Text('Histórico de Jogos',
style: TextStyle(
fontSize: 20 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
SizedBox(height: 16 * context.sf),
_selectedTeamName == "Selecionar Equipa"
? Container(
width: double.infinity,
padding: EdgeInsets.all(24.0 * context.sf),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color ??
Colors.white,
borderRadius:
BorderRadius.circular(16 * context.sf),
border: Border.all(
color: Colors.grey.withOpacity(0.1)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 10,
offset: const Offset(0, 4))
errorWidget:
(context, url, error) => Icon(
Icons.shield,
color: AppTheme.primaryRed,
size: 24 * context.sf),
),
)
: Icon(Icons.shield,
color: AppTheme.primaryRed,
size: 24 * context.sf),
SizedBox(width: 10 * context.sf),
Text(_selectedTeamName,
style: TextStyle(
fontSize: 16 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
]),
Icon(Icons.arrow_drop_down, color: textColor),
],
),
child: Column(
children: [
Container(
padding: EdgeInsets.all(18 * context.sf),
decoration: BoxDecoration(
color: AppTheme.primaryRed.withOpacity(0.08),
shape: BoxShape.circle),
child: Icon(Icons.shield_outlined,
color: AppTheme.primaryRed,
size: 42 * context.sf),
),
SizedBox(height: 20 * context.sf),
Text("Nenhuma Equipa Ativa",
style: TextStyle(
fontSize: 18 * context.sf,
fontWeight: FontWeight.bold,
color: textColor)),
SizedBox(height: 8 * context.sf),
Text(
"Escolha uma equipa no seletor acima para ver as estatísticas e o histórico.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13 * context.sf,
color: Colors.grey.shade600,
height: 1.4),
),
SizedBox(height: 24 * context.sf),
SizedBox(
width: double.infinity,
height: 48 * context.sf,
child: ElevatedButton.icon(
onPressed: () => _showTeamSelector(context),
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.primaryRed,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
10 * context.sf)),
),
icon: Icon(Icons.touch_app,
size: 20 * context.sf),
label: Text("Selecionar Agora",
style: TextStyle(
fontSize: 15 * context.sf,
fontWeight: FontWeight.bold)),
),
),
],
),
)
: StreamBuilder<List<Map<String, dynamic>>>(
stream: _supabase
.from('games')
.stream(primaryKey: ['id']).order('game_date',
ascending: false),
builder: (context, gameSnapshot) {
if (gameSnapshot.hasError) {
return Text("Erro: ${gameSnapshot.error}",
style:
const TextStyle(color: Colors.red));
}
if (!gameSnapshot.hasData &&
gameSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator());
}
final todosOsJogos = gameSnapshot.data ?? [];
final gamesList = todosOsJogos.where((game) {
String myT =
game['my_team']?.toString() ?? '';
String oppT =
game['opponent_team']?.toString() ?? '';
String status =
game['status']?.toString() ?? '';
return (myT == _selectedTeamName ||
oppT == _selectedTeamName) &&
status == 'Terminado';
}).take(3).toList();
if (gamesList.isEmpty) {
return Container(
width: double.infinity,
padding: EdgeInsets.all(20 * context.sf),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color,
borderRadius: BorderRadius.circular(14),
),
alignment: Alignment.center,
child: const Text(
"Ainda não há jogos terminados.",
style: TextStyle(color: Colors.grey)),
);
}
return Column(
children: gamesList.map((game) {
String dbMyTeam =
game['my_team']?.toString() ?? '';
String dbOppTeam =
game['opponent_team']?.toString() ?? '';
int dbMyScore = int.tryParse(
game['my_score']?.toString() ??
'0') ??
0;
int dbOppScore = int.tryParse(
game['opponent_score']
?.toString() ??
'0') ??
0;
String opponent;
int myScore;
int oppScore;
if (dbMyTeam == _selectedTeamName) {
opponent = dbOppTeam;
myScore = dbMyScore;
oppScore = dbOppScore;
} else {
opponent = dbMyTeam;
myScore = dbOppScore;
oppScore = dbMyScore;
}
String rawDate =
game['game_date']?.toString() ?? '---';
String date = rawDate.length >= 10
? rawDate.substring(0, 10)
: rawDate;
String result = myScore > oppScore
? 'V'
: (myScore < oppScore ? 'D' : 'E');
return _buildGameHistoryCard(
context: context,
opponent: opponent,
result: result,
myScore: myScore,
oppScore: oppScore,
date: date,
topPts: game['top_pts_name'] ?? '---',
topAst: game['top_ast_name'] ?? '---',
topRbs: game['top_rbs_name'] ?? '---',
topDef: game['top_def_name'] ?? '---',
mvp: game['mvp_name'] ?? '---',
);
}).toList(),
);
},
),
SizedBox(height: 20 * context.sf),
],
),
SizedBox(height: 20 * context.sf),
if (!isWide) ...[
statsSection,
SizedBox(height: 40 * context.sf),
historySection,
]
],
);
if (isWide) {
mainContent = Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(flex: 5, child: statsSection),
SizedBox(width: 20 * context.sf),
Expanded(flex: 6, child: historySection),
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
mainContent,
SizedBox(height: 20 * context.sf),
],
);
},
),
),
);