228 lines
11 KiB
Dart
228 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import '../controllers/team_controller.dart';
|
|
import '../utils/size_extension.dart'; // 👇 A MAGIA DO SF!
|
|
|
|
class StatusPage extends StatefulWidget {
|
|
const StatusPage({super.key});
|
|
|
|
@override
|
|
State<StatusPage> createState() => _StatusPageState();
|
|
}
|
|
|
|
class _StatusPageState extends State<StatusPage> {
|
|
final TeamController _teamController = TeamController();
|
|
final _supabase = Supabase.instance.client;
|
|
|
|
String? _selectedTeamId;
|
|
String _selectedTeamName = "Selecionar Equipa";
|
|
String _sortColumn = 'pts';
|
|
bool _isAscending = false;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.all(16.0 * context.sf),
|
|
child: InkWell(
|
|
onTap: () => _showTeamSelector(context),
|
|
child: Container(
|
|
padding: EdgeInsets.all(12 * context.sf),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(15 * context.sf),
|
|
border: Border.all(color: Colors.grey.shade300),
|
|
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 5)]
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(children: [
|
|
Icon(Icons.shield, color: const Color(0xFFE74C3C), size: 24 * context.sf),
|
|
SizedBox(width: 10 * context.sf),
|
|
Text(_selectedTeamName, style: TextStyle(fontSize: 16 * context.sf, fontWeight: FontWeight.bold))
|
|
]),
|
|
const Icon(Icons.arrow_drop_down),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
Expanded(
|
|
child: _selectedTeamId == null
|
|
? Center(child: Text("Seleciona uma equipa acima.", style: TextStyle(color: Colors.grey, fontSize: 14 * context.sf)))
|
|
: StreamBuilder<List<Map<String, dynamic>>>(
|
|
stream: _supabase.from('player_stats_with_names').stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!),
|
|
builder: (context, statsSnapshot) {
|
|
return StreamBuilder<List<Map<String, dynamic>>>(
|
|
stream: _supabase.from('games').stream(primaryKey: ['id']).eq('my_team', _selectedTeamName),
|
|
builder: (context, gamesSnapshot) {
|
|
return StreamBuilder<List<Map<String, dynamic>>>(
|
|
stream: _supabase.from('members').stream(primaryKey: ['id']).eq('team_id', _selectedTeamId!),
|
|
builder: (context, membersSnapshot) {
|
|
if (statsSnapshot.connectionState == ConnectionState.waiting || gamesSnapshot.connectionState == ConnectionState.waiting || membersSnapshot.connectionState == ConnectionState.waiting) {
|
|
return const Center(child: CircularProgressIndicator(color: Color(0xFFE74C3C)));
|
|
}
|
|
|
|
final membersData = membersSnapshot.data ?? [];
|
|
if (membersData.isEmpty) return Center(child: Text("Esta equipa não tem jogadores registados.", style: TextStyle(color: Colors.grey, fontSize: 14 * context.sf)));
|
|
|
|
final statsData = statsSnapshot.data ?? [];
|
|
final gamesData = gamesSnapshot.data ?? [];
|
|
final totalGamesPlayedByTeam = gamesData.where((g) => g['status'] == 'Terminado').length;
|
|
|
|
final List<Map<String, dynamic>> playerTotals = _aggregateStats(statsData, gamesData, membersData);
|
|
final teamTotals = _calculateTeamTotals(playerTotals, totalGamesPlayedByTeam);
|
|
|
|
playerTotals.sort((a, b) {
|
|
var valA = a[_sortColumn] ?? 0;
|
|
var valB = b[_sortColumn] ?? 0;
|
|
return _isAscending ? valA.compareTo(valB) : valB.compareTo(valA);
|
|
});
|
|
|
|
return _buildStatsGrid(context, playerTotals, teamTotals);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
List<Map<String, dynamic>> _aggregateStats(List<dynamic> stats, List<dynamic> games, List<dynamic> members) {
|
|
Map<String, Map<String, dynamic>> aggregated = {};
|
|
|
|
for (var member in members) {
|
|
String name = member['name']?.toString() ?? "Desconhecido";
|
|
aggregated[name] = {'name': name, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0};
|
|
}
|
|
|
|
for (var row in stats) {
|
|
String name = row['player_name']?.toString() ?? "Desconhecido";
|
|
if (!aggregated.containsKey(name)) aggregated[name] = {'name': name, 'j': 0, 'pts': 0, 'ast': 0, 'rbs': 0, 'stl': 0, 'blk': 0, 'mvp': 0, 'def': 0};
|
|
|
|
aggregated[name]!['j'] += 1;
|
|
aggregated[name]!['pts'] += (row['pts'] ?? 0);
|
|
aggregated[name]!['ast'] += (row['ast'] ?? 0);
|
|
aggregated[name]!['rbs'] += (row['rbs'] ?? 0);
|
|
aggregated[name]!['stl'] += (row['stl'] ?? 0);
|
|
aggregated[name]!['blk'] += (row['blk'] ?? 0);
|
|
}
|
|
|
|
for (var game in games) {
|
|
String? mvp = game['mvp_name'];
|
|
String? defRaw = game['top_def_name'];
|
|
if (mvp != null && aggregated.containsKey(mvp)) aggregated[mvp]!['mvp'] += 1;
|
|
if (defRaw != null) {
|
|
String defName = defRaw.split(' (')[0].trim();
|
|
if (aggregated.containsKey(defName)) aggregated[defName]!['def'] += 1;
|
|
}
|
|
}
|
|
return aggregated.values.toList();
|
|
}
|
|
|
|
Map<String, dynamic> _calculateTeamTotals(List<Map<String, dynamic>> players, int teamGames) {
|
|
int tPts = 0, tAst = 0, tRbs = 0, tStl = 0, tBlk = 0, tMvp = 0, tDef = 0;
|
|
for (var p in players) {
|
|
tPts += (p['pts'] as int); tAst += (p['ast'] as int); tRbs += (p['rbs'] as int); tStl += (p['stl'] as int); tBlk += (p['blk'] as int); tMvp += (p['mvp'] as int); tDef += (p['def'] as int);
|
|
}
|
|
return {'name': 'TOTAL EQUIPA', 'j': teamGames, 'pts': tPts, 'ast': tAst, 'rbs': tRbs, 'stl': tStl, 'blk': tBlk, 'mvp': tMvp, 'def': tDef};
|
|
}
|
|
|
|
Widget _buildStatsGrid(BuildContext context, List<Map<String, dynamic>> players, Map<String, dynamic> teamTotals) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: DataTable(
|
|
columnSpacing: 25 * context.sf,
|
|
headingRowColor: MaterialStateProperty.all(Colors.grey.shade100),
|
|
dataRowHeight: 60 * context.sf,
|
|
columns: [
|
|
DataColumn(label: const Text('JOGADOR')),
|
|
_buildSortableColumn(context, 'J', 'j'),
|
|
_buildSortableColumn(context, 'PTS', 'pts'),
|
|
_buildSortableColumn(context, 'AST', 'ast'),
|
|
_buildSortableColumn(context, 'RBS', 'rbs'),
|
|
_buildSortableColumn(context, 'STL', 'stl'),
|
|
_buildSortableColumn(context, 'BLK', 'blk'),
|
|
_buildSortableColumn(context, 'DEF 🛡️', 'def'),
|
|
_buildSortableColumn(context, 'MVP 🏆', 'mvp'),
|
|
],
|
|
rows: [
|
|
...players.map((player) => DataRow(cells: [
|
|
DataCell(Row(children: [CircleAvatar(radius: 15 * context.sf, backgroundColor: Colors.grey.shade200, child: Icon(Icons.person, size: 18 * context.sf)), SizedBox(width: 10 * context.sf), Text(player['name'], style: TextStyle(fontWeight: FontWeight.bold, fontSize: 13 * context.sf))])),
|
|
DataCell(Center(child: Text(player['j'].toString()))),
|
|
_buildStatCell(context, player['pts'], isHighlight: true),
|
|
_buildStatCell(context, player['ast']),
|
|
_buildStatCell(context, player['rbs']),
|
|
_buildStatCell(context, player['stl']),
|
|
_buildStatCell(context, player['blk']),
|
|
_buildStatCell(context, player['def'], isBlue: true),
|
|
_buildStatCell(context, player['mvp'], isGold: true),
|
|
])),
|
|
DataRow(
|
|
color: MaterialStateProperty.all(Colors.grey.shade50),
|
|
cells: [
|
|
DataCell(Text('TOTAL EQUIPA', style: TextStyle(fontWeight: FontWeight.w900, color: Colors.black, fontSize: 12 * context.sf))),
|
|
DataCell(Center(child: Text(teamTotals['j'].toString(), style: const TextStyle(fontWeight: FontWeight.bold)))),
|
|
_buildStatCell(context, teamTotals['pts'], isHighlight: true),
|
|
_buildStatCell(context, teamTotals['ast']),
|
|
_buildStatCell(context, teamTotals['rbs']),
|
|
_buildStatCell(context, teamTotals['stl']),
|
|
_buildStatCell(context, teamTotals['blk']),
|
|
_buildStatCell(context, teamTotals['def'], isBlue: true),
|
|
_buildStatCell(context, teamTotals['mvp'], isGold: true),
|
|
]
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
DataColumn _buildSortableColumn(BuildContext context, String title, String sortKey) {
|
|
return DataColumn(label: InkWell(
|
|
onTap: () => setState(() {
|
|
if (_sortColumn == sortKey) _isAscending = !_isAscending;
|
|
else { _sortColumn = sortKey; _isAscending = false; }
|
|
}),
|
|
child: Row(children: [
|
|
Text(title, style: TextStyle(fontSize: 12 * context.sf, fontWeight: FontWeight.bold)),
|
|
if (_sortColumn == sortKey) Icon(_isAscending ? Icons.arrow_drop_up : Icons.arrow_drop_down, size: 18 * context.sf, color: const Color(0xFFE74C3C)),
|
|
]),
|
|
));
|
|
}
|
|
|
|
DataCell _buildStatCell(BuildContext context, int value, {bool isHighlight = false, bool isGold = false, bool isBlue = false}) {
|
|
return DataCell(Center(child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 8 * context.sf, vertical: 4 * context.sf),
|
|
decoration: BoxDecoration(color: isGold && value > 0 ? Colors.amber.withOpacity(0.2) : (isBlue && value > 0 ? Colors.blue.withOpacity(0.1) : Colors.transparent), borderRadius: BorderRadius.circular(6)),
|
|
child: Text(value == 0 ? "-" : value.toString(), style: TextStyle(
|
|
fontWeight: (isHighlight || isGold || isBlue) ? FontWeight.w900 : FontWeight.w600,
|
|
fontSize: 14 * context.sf, color: isGold && value > 0 ? Colors.orange.shade900 : (isBlue && value > 0 ? Colors.blue.shade800 : (isHighlight ? Colors.green.shade700 : Colors.black87))
|
|
)),
|
|
)));
|
|
}
|
|
|
|
void _showTeamSelector(BuildContext context) {
|
|
showModalBottomSheet(context: context, builder: (context) => StreamBuilder<List<Map<String, dynamic>>>(
|
|
stream: _teamController.teamsStream,
|
|
builder: (context, snapshot) {
|
|
final teams = snapshot.data ?? [];
|
|
return ListView.builder(itemCount: teams.length, itemBuilder: (context, i) => ListTile(
|
|
title: Text(teams[i]['name']),
|
|
onTap: () { setState(() { _selectedTeamId = teams[i]['id']; _selectedTeamName = teams[i]['name']; }); Navigator.pop(context); },
|
|
));
|
|
},
|
|
));
|
|
}
|
|
} |