import 'dart:async'; import 'package:flutter/material.dart'; import 'package:insparkspokalen_ui/services/backend/LeaderBordSettingsService.dart'; import 'package:insparkspokalen_ui/models/teamModel.dart'; import 'package:insparkspokalen_ui/services/backend/teamService.dart'; import 'package:insparkspokalen_ui/services/googleAuthService.dart'; import 'package:insparkspokalen_ui/teams/teamImage.dart'; import 'package:provider/provider.dart'; import 'package:flutter_svg/svg.dart'; import '../services/backend/userService.dart'; class Leaderboard extends StatefulWidget { const Leaderboard({super.key}); @override State createState() => _LeaderboardState(); } class _LeaderboardState extends State { late TeamService teamService; late UserService userService; List groups = []; bool isLoading = true; final SettingsService settingsService = SettingsService(); bool showLeaderboard = true; TeamModel? userTeam; @override void initState() { super.initState(); teamService = Provider.of(context, listen: false); _loadGroups(); settingsService.getLeaderboardVisibility().then((value) { setState(() { showLeaderboard = value; }); }); Timer.periodic(const Duration(seconds: 10), (_) { _loadGroups(); }); } Future _loadGroups() async { final result = await teamService.showTeams(); result.sort((a, b) => b.score.compareTo(a.score)); final currentUser = GoogleAuthService.getCurrentUser(); TeamModel? fetchedUserTeam; if (currentUser != null) { int? teamId = await UserService().getUserTeamByEmail(currentUser.email); fetchedUserTeam = await teamService.getTeamById(teamId!); } setState(() { groups = result; userTeam = fetchedUserTeam; isLoading = false; }); } @override Widget build(BuildContext context) { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (showLeaderboard) Stack( alignment: Alignment.topCenter, children: [ // Halvcirkelbakgrund // Innehållet Padding( padding: const EdgeInsets.only(top: 100), child: isLoading ? const Center(child: CircularProgressIndicator()) : _TopplistaBody(teams: groups, userTeam: userTeam), ), ], ) else const Padding( padding: EdgeInsets.all(24.0), child: Center( child: Text( 'Leaderboard är för närvarande dold', style: TextStyle(color: Colors.white, fontSize: 18), ), ), ), ], ), ); } } class _GreyBackground extends StatelessWidget { final Widget child; const _GreyBackground({required this.child}); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: const Color(0xFFDAA520), borderRadius: BorderRadius.circular(40), ), child: child, ); } } class _TopplistaBody extends StatelessWidget { final List teams; final TeamModel? userTeam; const _TopplistaBody({required this.teams, required this.userTeam}); @override Widget build(BuildContext context) { //if (teams.length < 3) return const SizedBox(); int? userPlace; if (userTeam != null) { final match = teams.indexWhere((t) => t.teamId == userTeam!.teamId); if (match != -1) { userPlace = match + 1; } } return Column( children: [ PodiumWidget(topTeams: teams), const SizedBox(height: 24), ...teams .skip(3) .toList() .asMap() .entries .map( (entry) => GruppKort.GroupCard(group: entry.value, place: entry.key + 4), ), if (userTeam != null && userPlace != null) Padding( padding: const EdgeInsets.only(top: 32.0), child: Text( 'Ditt lag: ${userTeam!.name} ligger på plats $userPlace med ${userTeam!.score} poäng', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white, ), textAlign: TextAlign.center, ), ), ], ); } } class GruppKort extends StatelessWidget { final TeamModel group; final int place; const GruppKort.GroupCard({ super.key, required this.group, required this.place, }); /* Color _getMedalColor(int place) { switch (place) { case 1: return Colors.amber; case 2: return Color(0xFFC0C0C0); // silver case 3: return Color(0xFFCD7F32); // bronze default: return Colors.transparent; } } */ @override Widget build(BuildContext context) { Widget leadingWidget; if (place <= 3) { leadingWidget = Stack( alignment: Alignment.center, children: [ //Icon(Icons.emoji_events, color: _getMedalColor(place), size: 40), Text( '$place', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, ), ), ], ); } else { leadingWidget = CircleAvatar( backgroundColor: Colors.white, radius: 30, child: group.imageUrl == null || group.imageUrl!.isEmpty ? Text( '$place', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, ), ) : TeamImage(imageUrl: group.imageUrl, size: 30), ); } return Card( elevation: 3, margin: const EdgeInsets.symmetric(vertical: 8.0), color: Colors.grey[900], shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: 16.0, vertical: 8.0, ), leading: leadingWidget, title: Text( group.name, style: const TextStyle(fontSize: 18, color: Colors.white), ), trailing: Text( '${group.score} poäng', style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ); } } class PodiumWidget extends StatelessWidget { final List topTeams; const PodiumWidget({super.key, required this.topTeams}); @override Widget build(BuildContext context) { if (topTeams.isEmpty) return const SizedBox.shrink(); // Sortera och skapa en lista med exakt tre element (fyll med null om färre) final sorted = List.from(topTeams) ..sort((a, b) => b.score.compareTo(a.score)); final List podiumTeams = List.generate( 3, (i) => i < sorted.length ? sorted[i] : null, ); final podiumHeights = [100.0, 140.0, 100.0]; Color getPlaceColor(int place) { switch (place) { case 1: return const Color(0xFF1F1F1F); case 2: case 3: return const Color(0xFF262626); default: return Colors.grey; } } return Column( children: [ const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: List.generate(3, (index) { final team = index == 0 ? podiumTeams[1] // plats 2 : index == 1 ? podiumTeams[0] // plats 1 : podiumTeams[2]; // plats 3 final place = index == 0 ? 2 : index == 1 ? 1 : 3; return Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Column( mainAxisSize: MainAxisSize.min, children: [ Stack( alignment: Alignment.bottomCenter, clipBehavior: Clip.none, children: [ CircleAvatar( backgroundColor: Colors.white, radius: 35, child: team != null ? TeamImage(imageUrl: team.imageUrl, size: 40) : const Icon( Icons.group_off, color: Colors.grey, ), ), Positioned( bottom: -6, child: DownArrow(color: Colors.white, size: 12), ), ], ), const SizedBox(height: 8), Container( height: podiumHeights[index], width: 100, decoration: BoxDecoration( color: getPlaceColor(place), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white10), ), padding: const EdgeInsets.all(8), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( team != null ? '${team.score} poäng' : '–', style: const TextStyle( color: Colors.white70, fontSize: 12, fontWeight: FontWeight.w500, ), ), Text( '$place', style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), ), const SizedBox(height: 4), Text( team != null ? team.name : '-', textAlign: TextAlign.center, style: const TextStyle(color: Colors.white), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ); }), ), ], ); } } class DownArrow extends StatelessWidget { final Color color; final double size; const DownArrow({required this.color, this.size = 12}); @override Widget build(BuildContext context) { return ClipPath( clipper: _DownArrowClipper(), child: Container(width: size, height: size, color: color), ); } } class _DownArrowClipper extends CustomClipper { @override Path getClip(Size size) { final path = Path(); path.moveTo(0, 0); path.lineTo(size.width / 2, size.height); path.lineTo(size.width, 0); path.close(); return path; } @override bool shouldReclip(CustomClipper oldClipper) => false; }