Add Group Frontend
Dieser Commit ist enthalten in:
Ursprung
cdca1c8562
Commit
3cfdbbcdbd
@ -17,6 +17,12 @@ final eventsListProvider = FutureProvider<List<ShortEvent>>((ref) async {
|
||||
return repo.listEvents();
|
||||
}, dependencies: [eventRepositoryProvider]);
|
||||
|
||||
final groupsProvider = FutureProvider.autoDispose<List<String>>((ref) async {
|
||||
return (await ref.watch(httpClient.future)).get("/data/groups").then((value) {
|
||||
return (value.data as List).map((e) => e as String).toList();
|
||||
});
|
||||
}, dependencies: [httpClient]);
|
||||
|
||||
class EventRepository {
|
||||
final Dio _client;
|
||||
|
||||
@ -71,15 +77,8 @@ class EventRepository {
|
||||
await _client.delete("/fights/$id");
|
||||
}
|
||||
|
||||
Future<void> createFight(
|
||||
int eventId,
|
||||
DateTime start,
|
||||
String mode,
|
||||
String map,
|
||||
Team blue,
|
||||
Team red,
|
||||
int referee,
|
||||
) {
|
||||
Future<void> createFight(int eventId, DateTime start, String mode, String map,
|
||||
Team blue, Team red, int referee, String? group) {
|
||||
return _client.post("/fights", data: {
|
||||
"event": eventId,
|
||||
"spielmodus": mode,
|
||||
@ -88,6 +87,7 @@ class EventRepository {
|
||||
"blueTeam": blue.id,
|
||||
"redTeam": red.id,
|
||||
"kampfleiter": referee,
|
||||
"group": group ?? "null",
|
||||
});
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ class EventRepository {
|
||||
}
|
||||
|
||||
Future<void> updateFight(int id, DateTime start, String mode, String map,
|
||||
int referee, Team blue, Team red) {
|
||||
int referee, Team blue, Team red, String? group) {
|
||||
return _client.put("/fights/$id", data: {
|
||||
"spielmodus": mode,
|
||||
"map": map,
|
||||
@ -105,6 +105,7 @@ class EventRepository {
|
||||
"kampfleiter": referee,
|
||||
"blueTeam": blue.id,
|
||||
"redTeam": red.id,
|
||||
"group": group ?? "null",
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -118,9 +119,10 @@ class EventFight {
|
||||
final Team blueTeam;
|
||||
final User kampfleiter;
|
||||
final int score;
|
||||
final String? group;
|
||||
|
||||
EventFight(this.id, this.gameMode, this.map, this.start, this.redTeam,
|
||||
this.blueTeam, this.kampfleiter, this.score);
|
||||
this.blueTeam, this.kampfleiter, this.score, this.group);
|
||||
|
||||
Team get winner {
|
||||
return getTeamWithContextColor(Colors.white);
|
||||
@ -150,7 +152,8 @@ class EventFight {
|
||||
Team.fromJson(json["redTeam"]),
|
||||
Team.fromJson(json["blueTeam"]),
|
||||
User.fromJson(json["kampfleiter"]),
|
||||
json["ergebnis"]);
|
||||
json["ergebnis"],
|
||||
json["group"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:grouped_list/grouped_list.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:steamwar_multitool/src/util/event_fight_exporter.dart';
|
||||
@ -414,7 +415,7 @@ class _EventFightList extends HookConsumerWidget {
|
||||
[date.value, lowest]);
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text("Reshedule Fights"),
|
||||
title: const Text("Reschedule Fights"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -448,14 +449,15 @@ class _EventFightList extends HookConsumerWidget {
|
||||
final repo = await ref
|
||||
.read(eventRepositoryProvider.future);
|
||||
for (final fight in selectedFights) {
|
||||
repo.updateFight(
|
||||
await repo.updateFight(
|
||||
fight.id,
|
||||
fight.start.add(offset),
|
||||
fight.gameMode,
|
||||
fight.map,
|
||||
fight.kampfleiter.id,
|
||||
fight.blueTeam,
|
||||
fight.redTeam);
|
||||
fight.redTeam,
|
||||
fight.group);
|
||||
}
|
||||
update();
|
||||
Navigator.of(context).pop();
|
||||
@ -490,14 +492,15 @@ class _EventFightList extends HookConsumerWidget {
|
||||
await ref.read(eventRepositoryProvider.future);
|
||||
for (final fight in fights.value) {
|
||||
if (selected.value.contains(fight.id)) {
|
||||
repo.updateFight(
|
||||
await repo.updateFight(
|
||||
fight.id,
|
||||
fight.start,
|
||||
fight.gameMode,
|
||||
fight.map,
|
||||
kampfleiter.id,
|
||||
fight.blueTeam,
|
||||
fight.redTeam);
|
||||
fight.redTeam,
|
||||
fight.group);
|
||||
}
|
||||
}
|
||||
update();
|
||||
@ -505,6 +508,42 @@ class _EventFightList extends HookConsumerWidget {
|
||||
},
|
||||
icon: const Icon(Icons.person)),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Change Group",
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
if (selected.value.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final group = await showSearch(
|
||||
context: context,
|
||||
delegate: GroupSearchDelegate(
|
||||
await ref.read(groupsProvider.future)));
|
||||
|
||||
if (group == null) {
|
||||
return;
|
||||
} else {
|
||||
final repo =
|
||||
await ref.read(eventRepositoryProvider.future);
|
||||
for (final fight in fights.value) {
|
||||
if (selected.value.contains(fight.id)) {
|
||||
await repo.updateFight(
|
||||
fight.id,
|
||||
fight.start,
|
||||
fight.gameMode,
|
||||
fight.map,
|
||||
fight.kampfleiter.id,
|
||||
fight.blueTeam,
|
||||
fight.redTeam,
|
||||
group);
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.group)),
|
||||
),
|
||||
Tooltip(
|
||||
message: "Delete Fights",
|
||||
child: IconButton(
|
||||
@ -531,7 +570,7 @@ class _EventFightList extends HookConsumerWidget {
|
||||
final repo = await ref
|
||||
.read(eventRepositoryProvider.future);
|
||||
for (final fight in selected.value) {
|
||||
repo.deleteFight(fight);
|
||||
await repo.deleteFight(fight);
|
||||
}
|
||||
update();
|
||||
Navigator.of(context).pop();
|
||||
@ -579,7 +618,8 @@ class _EventFightList extends HookConsumerWidget {
|
||||
openExportedFights(fights.value, event.name);
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
@ -588,8 +628,51 @@ class _EventFightList extends HookConsumerWidget {
|
||||
const Center(
|
||||
child: Text("No fights yet"),
|
||||
),
|
||||
for (final fight in fights.value)
|
||||
ListTile(
|
||||
GroupedListView(
|
||||
elements: fights.value,
|
||||
groupBy: (fight) => fight.group ?? "Ungrouped",
|
||||
shrinkWrap: true,
|
||||
itemComparator: (fight1, fight2) =>
|
||||
fight1.start.compareTo(fight2.start),
|
||||
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||
floatingHeader: true,
|
||||
groupSeparatorBuilder: (group) => InkWell(
|
||||
onTap: () {
|
||||
final g = group == "Ungrouped" ? null : group;
|
||||
final isAllSelected = selected.value
|
||||
.where((element) =>
|
||||
fights.value
|
||||
.firstWhere((element2) => element2.id == element)
|
||||
.group ==
|
||||
g)
|
||||
.length ==
|
||||
fights.value.where((element) => element.group == g).length;
|
||||
if (isAllSelected) {
|
||||
selected.value = selected.value
|
||||
.where((element) =>
|
||||
fights.value
|
||||
.firstWhere((element2) => element2.id == element)
|
||||
.group !=
|
||||
g)
|
||||
.toList();
|
||||
} else {
|
||||
selected.value = [
|
||||
...selected.value,
|
||||
...fights.value
|
||||
.where((element) => element.group == g)
|
||||
.map((e) => e.id)
|
||||
];
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
group,
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, fight) => ListTile(
|
||||
title: Text(
|
||||
"${fight.blueTeam.name} vs ${fight.redTeam.name}",
|
||||
style: TextStyle(
|
||||
@ -639,7 +722,8 @@ class _EventFightList extends HookConsumerWidget {
|
||||
.toList();
|
||||
}
|
||||
}),
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -665,6 +749,7 @@ class EditEventFightDialog extends HookConsumerWidget {
|
||||
final mode = useState(fight.gameMode);
|
||||
final map = useState(fight.map);
|
||||
final referrer = useState(fight.kampfleiter);
|
||||
final group = useState(fight.group ?? "");
|
||||
|
||||
return AlertDialog(
|
||||
title: Text("${fight.blueTeam.name} vs ${fight.redTeam.name}"),
|
||||
@ -741,6 +826,24 @@ class EditEventFightDialog extends HookConsumerWidget {
|
||||
? "None"
|
||||
: "${referrer.value.name} (${referrer.value!.id})"),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text("Group"),
|
||||
const SizedBox(height: 8),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final selGroup = await showSearch(
|
||||
context: context,
|
||||
delegate: GroupSearchDelegate(
|
||||
await ref.read(groupsProvider.future)));
|
||||
if (selGroup != null) {
|
||||
if (selGroup.isEmpty) {
|
||||
group.value = "";
|
||||
} else {
|
||||
group.value = selGroup;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(group.value.isEmpty ? "None" : group.value)),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
@ -784,8 +887,15 @@ class EditEventFightDialog extends HookConsumerWidget {
|
||||
onPressed: () async {
|
||||
var nav = Navigator.of(context);
|
||||
final repo = await ref.read(eventRepositoryProvider.future);
|
||||
await repo.updateFight(fight.id, start.value, mode.value,
|
||||
map.value, referrer.value.id, blueTeam.value, redTeam.value);
|
||||
await repo.updateFight(
|
||||
fight.id,
|
||||
start.value,
|
||||
mode.value,
|
||||
map.value,
|
||||
referrer.value.id,
|
||||
blueTeam.value,
|
||||
redTeam.value,
|
||||
group.value.isEmpty ? null : group.value);
|
||||
fightsRefresher();
|
||||
nav.pop();
|
||||
},
|
||||
@ -813,6 +923,7 @@ class _AddFightWidget extends HookConsumerWidget {
|
||||
final redTeam = useState<Team?>(null);
|
||||
final date = useState<DateTime>(event.start);
|
||||
final referrer = useState<User?>(null);
|
||||
final group = useState<String>("");
|
||||
|
||||
final canCreate = useMemoized(() {
|
||||
return gamemode.value != null &&
|
||||
@ -883,6 +994,24 @@ class _AddFightWidget extends HookConsumerWidget {
|
||||
? "None"
|
||||
: "${referrer.value!.name} (${referrer.value!.id})"),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text("Group"),
|
||||
const SizedBox(height: 8),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final selGroup = await showSearch(
|
||||
context: context,
|
||||
delegate: GroupSearchDelegate(
|
||||
await ref.read(groupsProvider.future)));
|
||||
if (selGroup != null) {
|
||||
if (selGroup.isEmpty) {
|
||||
group.value = "";
|
||||
} else {
|
||||
group.value = selGroup;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(group.value.isEmpty ? "None" : group.value)),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
@ -903,7 +1032,8 @@ class _AddFightWidget extends HookConsumerWidget {
|
||||
map.value!,
|
||||
blueTeam.value!,
|
||||
redTeam.value!,
|
||||
referrer.value?.id ?? 0);
|
||||
referrer.value?.id ?? 0,
|
||||
group.value.isEmpty ? null : group.value);
|
||||
onAdded.call();
|
||||
nav.pop();
|
||||
}
|
||||
|
@ -317,3 +317,69 @@ class TeamSearchDelegate extends SearchDelegate<Team?> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GroupSearchDelegate extends SearchDelegate<String?> {
|
||||
final List<String> groups;
|
||||
|
||||
GroupSearchDelegate(this.groups);
|
||||
|
||||
@override
|
||||
List<Widget>? buildActions(BuildContext context) {
|
||||
return [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
query = "";
|
||||
},
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget? buildLeading(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
close(context, null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
return buildSuggestions(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(query),
|
||||
onTap: query.isEmpty
|
||||
? null
|
||||
: () {
|
||||
close(context, query);
|
||||
},
|
||||
subtitle: const Text("Create new group"),
|
||||
leading: const Icon(Icons.add),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.clear),
|
||||
title: const Text("Reset"),
|
||||
onTap: () {
|
||||
close(context, "");
|
||||
},
|
||||
),
|
||||
for (final group in groups)
|
||||
if (group.toLowerCase().contains(query.toLowerCase()))
|
||||
ListTile(
|
||||
title: Text(group),
|
||||
onTap: () {
|
||||
close(context, group);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -282,6 +282,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
grouped_list:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: grouped_list
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.2"
|
||||
hooks_riverpod:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -23,6 +23,7 @@ dependencies:
|
||||
go_router: ^6.0.0
|
||||
intl: ^0.18.0
|
||||
csv: ^5.0.1
|
||||
grouped_list: ^5.1.2
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren