add carousel view
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { Team, TournamentTeam, TeamMember } from '../models/team';
|
import { Team, TournamentTeam, TeamMember } from '../models/team';
|
||||||
|
import { Tournament } from '../models/tournament';
|
||||||
import { Database, RunResult } from 'sqlite3';
|
import { Database, RunResult } from 'sqlite3';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@@ -125,12 +126,16 @@ export class TeamService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTournamentsByTeamId(teamId: number): Promise<TournamentTeam[]> {
|
getTournamentsByTeamId(teamId: number): Promise<Tournament[]> {
|
||||||
return new Promise<TournamentTeam[]>((resolve, reject) => {
|
return new Promise<Tournament[]>((resolve, reject) => {
|
||||||
this.db.all(
|
this.db.all(
|
||||||
`SELECT * FROM TournamentTeams WHERE teamId = ?`,
|
`SELECT t.*,
|
||||||
|
(SELECT COUNT(*) FROM TournamentTeams WHERE tournamentId = t.id) as currentTeamAmount
|
||||||
|
FROM Tournaments t
|
||||||
|
INNER JOIN TournamentTeams tt ON t.id = tt.tournamentId
|
||||||
|
WHERE tt.teamId = ?`,
|
||||||
[teamId],
|
[teamId],
|
||||||
(err: Error | null, rows: TournamentTeam[]) => {
|
(err: Error | null, rows: Tournament[]) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
resolve(rows);
|
resolve(rows);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,3 +53,17 @@ Folgende Dateien wurden in diesem Prompt verändert:
|
|||||||
- Implement auth-aware router to keep users logged in after app restart.<br><br>
|
- Implement auth-aware router to keep users logged in after app restart.<br><br>
|
||||||
Folgende Dateien wurden in diesem Prompt verändert:
|
Folgende Dateien wurden in diesem Prompt verändert:
|
||||||
- frontend_splatournament_manager/lib/main.dart
|
- frontend_splatournament_manager/lib/main.dart
|
||||||
|
|
||||||
|
- Create a carousel on the homepage that shows all the tournaments that one of your teams is participating in.<br><br>
|
||||||
|
Folgende Dateien wurden in diesem Prompt verändert:
|
||||||
|
- frontend_splatournament_manager/lib/providers/team_provider.dart
|
||||||
|
- frontend_splatournament_manager/lib/widgets/my_tournaments_carousel.dart (neu erstellt)
|
||||||
|
- frontend_splatournament_manager/lib/pages/home_page.dart
|
||||||
|
|
||||||
|
- Fix getTournamentsByTeam endpoint to return full Tournament objects instead of TournamentTeam objects.<br><br>
|
||||||
|
Folgende Dateien wurden in diesem Prompt verändert:
|
||||||
|
- backend_splatournament_manager/src/services/team-service.ts (changed return type from any[] to Tournament[])
|
||||||
|
|
||||||
|
- Add navigation to tournament details in the carousel.<br><br>
|
||||||
|
Folgende Dateien wurden in diesem Prompt verändert:
|
||||||
|
- frontend_splatournament_manager/lib/widgets/my_tournaments_carousel.dart
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:frontend_splatournament_manager/providers/team_provider.dart';
|
|||||||
import 'package:frontend_splatournament_manager/widgets/available_tournament_list.dart';
|
import 'package:frontend_splatournament_manager/widgets/available_tournament_list.dart';
|
||||||
import 'package:frontend_splatournament_manager/widgets/teams_list_widget.dart';
|
import 'package:frontend_splatournament_manager/widgets/teams_list_widget.dart';
|
||||||
import 'package:frontend_splatournament_manager/widgets/my_teams_widget.dart';
|
import 'package:frontend_splatournament_manager/widgets/my_teams_widget.dart';
|
||||||
|
import 'package:frontend_splatournament_manager/widgets/my_tournaments_carousel.dart';
|
||||||
import 'package:frontend_splatournament_manager/pages/create_tournament_page.dart';
|
import 'package:frontend_splatournament_manager/pages/create_tournament_page.dart';
|
||||||
import 'package:frontend_splatournament_manager/pages/create_team_page.dart';
|
import 'package:frontend_splatournament_manager/pages/create_team_page.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@@ -91,9 +92,11 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
|
|||||||
index: _selectedIndex,
|
index: _selectedIndex,
|
||||||
children: [
|
children: [
|
||||||
// Tournaments View
|
// Tournaments View
|
||||||
Container(
|
Column(
|
||||||
padding: const EdgeInsets.fromLTRB(0, 12, 0, 36),
|
children: [
|
||||||
child: Column(children: [const Spacer(), const AvailableTournamentList()]),
|
const MyTournamentsCarousel(),
|
||||||
|
const Expanded(child: AvailableTournamentList()),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
// Teams View with tabs
|
// Teams View with tabs
|
||||||
TabBarView(
|
TabBarView(
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("Tournament"),
|
title: Text(widget.tournament.name, style: TextStyle(overflow: TextOverflow.ellipsis)),
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface.withAlpha(180),
|
backgroundColor: Theme.of(context).colorScheme.surface.withAlpha(180),
|
||||||
elevation: 3,
|
elevation: 3,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@@ -88,5 +88,21 @@ class TeamProvider extends ChangeNotifier {
|
|||||||
Future<List<Map<String, dynamic>>> getTeamMembers(int teamId) {
|
Future<List<Map<String, dynamic>>> getTeamMembers(int teamId) {
|
||||||
return _teamService.getTeamMembers(teamId);
|
return _teamService.getTeamMembers(teamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Map<String, dynamic>>> getMyTeamsTournaments() async {
|
||||||
|
final userTeams = await getUserTeams();
|
||||||
|
final Set<Map<String, dynamic>> tournamentsSet = {};
|
||||||
|
|
||||||
|
for (final team in userTeams) {
|
||||||
|
try {
|
||||||
|
final tournaments = await _teamService.getTournamentsByTeam(team.id);
|
||||||
|
tournamentsSet.addAll(tournaments);
|
||||||
|
} catch (e) {
|
||||||
|
// If a team has no tournaments, continue with others
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tournamentsSet.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:frontend_splatournament_manager/providers/team_provider.dart';
|
||||||
|
import 'package:frontend_splatournament_manager/models/tournament.dart';
|
||||||
|
import 'package:frontend_splatournament_manager/pages/tournament_detail_page.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class MyTournamentsCarousel extends StatelessWidget {
|
||||||
|
const MyTournamentsCarousel({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<List<Map<String, dynamic>>>(
|
||||||
|
future: Provider.of<TeamProvider>(context, listen: false).getMyTeamsTournaments(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const SizedBox(
|
||||||
|
height: 180,
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final tournaments = snapshot.data ?? [];
|
||||||
|
|
||||||
|
if (tournaments.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||||
|
child: Text(
|
||||||
|
'My Tournaments',
|
||||||
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 150,
|
||||||
|
child: PageView.builder(
|
||||||
|
controller: PageController(viewportFraction: 0.9),
|
||||||
|
itemCount: tournaments.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final tournament = Tournament.fromJson(tournaments[index]);
|
||||||
|
return _TournamentCard(tournament: tournament);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TournamentCard extends StatelessWidget {
|
||||||
|
final Tournament tournament;
|
||||||
|
|
||||||
|
const _TournamentCard({required this.tournament});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
elevation: 4,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => TournamentDetailPage(tournament: tournament),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.emoji_events, size: 32),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
tournament.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
tournament.description,
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
tournament.isRegistrationOpen
|
||||||
|
? Icons.check_circle
|
||||||
|
: Icons.cancel,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
tournament.isRegistrationOpen
|
||||||
|
? 'Registration Open'
|
||||||
|
: 'Registration Closed',
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
'${tournament.currentTeamAmount}/${tournament.maxTeamAmount} teams',
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user