Logout button for userProfile and AdminProfile Preliminary endpoints for backend to create and fetch user Removed authguard Refactored loginGoogle to googleAuthService.dart
257 lines
7.1 KiB
Dart
257 lines
7.1 KiB
Dart
import 'dart:async';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:insparkspokalen_ui/models/teamModel.dart';
|
|
import 'package:insparkspokalen_ui/services/teamService.dart';
|
|
import 'package:insparkspokalen_ui/services/googleAuthService.dart';
|
|
|
|
class LeaderboardAdmin extends StatefulWidget {
|
|
const LeaderboardAdmin({super.key});
|
|
|
|
@override
|
|
State<LeaderboardAdmin> createState() => _LeaderboardState();
|
|
}
|
|
|
|
class _LeaderboardState extends State<LeaderboardAdmin> {
|
|
late TeamService groupService;
|
|
List<TeamModel> groups = [];
|
|
bool isLoading = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
groupService = Provider.of<TeamService>(context, listen: false);
|
|
_loadGroups();
|
|
|
|
// Uppdatera var 10:e sekund
|
|
Timer.periodic(Duration(seconds: 10), (_) {
|
|
_loadGroups();
|
|
});
|
|
}
|
|
|
|
|
|
Future<void> _loadGroups() async {
|
|
final result = await groupService.showTeams();
|
|
setState(() {
|
|
groups = result;
|
|
isLoading = false;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const _Heading(),
|
|
_GreyBackground(
|
|
child: isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: _TopplistaBody(groups: groups),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Heading extends StatelessWidget {
|
|
const _Heading();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 16.0),
|
|
child: Text(
|
|
'Hantera poäng',
|
|
style: TextStyle(
|
|
color: Color(0xFFDAA520),
|
|
fontSize: 32,
|
|
fontFamily: 'Nunito',
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
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(0xFF6D6D6D),
|
|
borderRadius: BorderRadius.circular(40),
|
|
),
|
|
child: child,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TopplistaBody extends StatelessWidget {
|
|
final List<TeamModel> groups;
|
|
|
|
const _TopplistaBody({required this.groups});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
...groups
|
|
.asMap()
|
|
.entries
|
|
.map((entry) => GruppKort.GroupCard(
|
|
group: entry.value,
|
|
place: entry.key + 1,
|
|
))
|
|
,
|
|
const SizedBox(height: 16),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class GruppKort extends StatefulWidget {
|
|
final TeamModel group;
|
|
final int place;
|
|
|
|
const GruppKort.GroupCard({super.key, required this.group, required this.place});
|
|
|
|
@override
|
|
State<GruppKort> createState() => _GruppKortState();
|
|
}
|
|
|
|
class _GruppKortState extends State<GruppKort> {
|
|
late TextEditingController _controller;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = TextEditingController(text: '0');
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
final groupService = TeamService();
|
|
|
|
Future<void> _updateScore(int scoreChange) async {
|
|
final int? score = int.tryParse(_controller.text);
|
|
if (score == null) return;
|
|
|
|
final int updatedScore = scoreChange == 1 ? score : -score;
|
|
|
|
final success = await groupService.updateScore(widget.group.id, updatedScore);
|
|
|
|
if (success) {
|
|
setState(() {
|
|
_controller.text = '0';
|
|
});
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Poäng uppdaterad för ${widget.group.name}')),
|
|
);
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Misslyckades att uppdatera poäng')),
|
|
);
|
|
}
|
|
}
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
elevation: 3,
|
|
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
|
color: const Color(0xFF2E2E2E),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(12.0),
|
|
child: Column(
|
|
children: [
|
|
ListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
title: Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
widget.group.name,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
color: Color(0xFFD6D6D6),
|
|
),
|
|
),
|
|
),
|
|
trailing: Text(
|
|
'${widget.group.score} poäng',
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFFD6D6D6),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextField(
|
|
controller: _controller,
|
|
keyboardType: TextInputType.number,
|
|
style: const TextStyle(color: Colors.white),
|
|
decoration: InputDecoration(
|
|
labelText: 'Ange poängändring',
|
|
labelStyle: const TextStyle(color: Colors.white),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: const BorderSide(color: Color(0xFFDAA520)),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: const BorderSide(color: Color(0xFFDAA520), width: 2),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
|
|
IconButton(
|
|
icon: const Icon(Icons.add, color: Color(0xFFDAA520)),
|
|
onPressed: () async {
|
|
if (!GoogleAuthService.isLoggedIn()) {
|
|
await GoogleAuthService.signInWithGoogle(context);
|
|
if (!GoogleAuthService.isLoggedIn()) return; // user cancelled or login failed
|
|
}
|
|
_updateScore(1);
|
|
},
|
|
),
|
|
const SizedBox(width: 8),
|
|
|
|
IconButton(
|
|
icon: const Icon(Icons.remove, color: Color(0xFFDAA520)),
|
|
onPressed: () async {
|
|
if (!GoogleAuthService.isLoggedIn()) {
|
|
await GoogleAuthService.signInWithGoogle(context);
|
|
if (!GoogleAuthService.isLoggedIn()) return; // user cancelled or login failed
|
|
}
|
|
_updateScore(1);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |