WIP: Bearbeiten und Löschen von Matches #171
@@ -22,9 +22,15 @@
|
|||||||
"days_ago": "vor {count} Tagen",
|
"days_ago": "vor {count} Tagen",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"delete_all_data": "Alle Daten löschen",
|
"delete_all_data": "Alle Daten löschen",
|
||||||
"delete_group": "Gruppe löschen",
|
"delete_group": "Diese Gruppe löschen",
|
||||||
"edit_group": "Gruppe bearbeiten",
|
"edit_group": "Gruppe bearbeiten",
|
||||||
|
"delete_group": "Gruppe löschen",
|
||||||
|
"delete_match": "Spiel löschen",
|
||||||
|
"edit_group": "Gruppe bearbeiten",
|
||||||
|
"enter_results": "Ergebnisse eintragen",
|
||||||
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
|
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
|
||||||
|
"error_deleting_group": "Fehler beim Löschen der Gruppe, bitte erneut versuchen",
|
||||||
|
"error_editing_group": "Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen",
|
||||||
"error_reading_file": "Fehler beim Lesen der Datei",
|
"error_reading_file": "Fehler beim Lesen der Datei",
|
||||||
"export_canceled": "Export abgebrochen",
|
"export_canceled": "Export abgebrochen",
|
||||||
"export_data": "Daten exportieren",
|
"export_data": "Daten exportieren",
|
||||||
@@ -46,6 +52,7 @@
|
|||||||
"licenses": "Lizenzen",
|
"licenses": "Lizenzen",
|
||||||
"match_in_progress": "Spiel läuft...",
|
"match_in_progress": "Spiel läuft...",
|
||||||
"match_name": "Spieltitel",
|
"match_name": "Spieltitel",
|
||||||
|
"match_profile": "Spielprofil",
|
||||||
"matches": "Spiele",
|
"matches": "Spiele",
|
||||||
"members": "Mitglieder",
|
"members": "Mitglieder",
|
||||||
"most_points": "Höchste Punkte",
|
"most_points": "Höchste Punkte",
|
||||||
@@ -58,6 +65,7 @@
|
|||||||
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
|
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
|
||||||
"no_players_selected": "Keine Spieler:innen ausgewählt",
|
"no_players_selected": "Keine Spieler:innen ausgewählt",
|
||||||
"no_recent_matches_available": "Keine letzten Spiele verfügbar",
|
"no_recent_matches_available": "Keine letzten Spiele verfügbar",
|
||||||
|
"no_results_entered_yet": "Noch keine Ergebnisse eingetragen",
|
||||||
"no_second_match_available": "Kein zweites Spiel verfügbar",
|
"no_second_match_available": "Kein zweites Spiel verfügbar",
|
||||||
"no_statistics_available": "Keine Statistiken verfügbar",
|
"no_statistics_available": "Keine Statistiken verfügbar",
|
||||||
"none": "Kein",
|
"none": "Kein",
|
||||||
@@ -70,11 +78,14 @@
|
|||||||
"privacy_policy": "Datenschutzerklärung",
|
"privacy_policy": "Datenschutzerklärung",
|
||||||
"quick_create": "Schnellzugriff",
|
"quick_create": "Schnellzugriff",
|
||||||
"recent_matches": "Letzte Spiele",
|
"recent_matches": "Letzte Spiele",
|
||||||
|
"result": "Ergebnis",
|
||||||
|
"results": "Ergebnisse",
|
||||||
"ruleset": "Regelwerk",
|
"ruleset": "Regelwerk",
|
||||||
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.",
|
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.",
|
||||||
"ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.",
|
"ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.",
|
||||||
"ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.",
|
"ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.",
|
||||||
"ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
|
"ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
|
||||||
|
"save_changes": "Änderungen speichern",
|
||||||
"search_for_groups": "Nach Gruppen suchen",
|
"search_for_groups": "Nach Gruppen suchen",
|
||||||
"search_for_players": "Nach Spieler:innen suchen",
|
"search_for_players": "Nach Spieler:innen suchen",
|
||||||
"select_winner": "Gewinner:in wählen:",
|
"select_winner": "Gewinner:in wählen:",
|
||||||
|
|||||||
@@ -37,10 +37,10 @@
|
|||||||
"description": "Button text to create a match"
|
"description": "Button text to create a match"
|
||||||
},
|
},
|
||||||
"@create_new_group": {
|
"@create_new_group": {
|
||||||
"description": "Button text to create a new group"
|
"description": "Appbar text to create a new group"
|
||||||
},
|
},
|
||||||
"@create_new_match": {
|
"@create_new_match": {
|
||||||
"description": "Button text to create a new match"
|
"description": "Appbar text to create a new match"
|
||||||
},
|
},
|
||||||
"@created_on": {
|
"@created_on": {
|
||||||
"description": "Label for creation date"
|
"description": "Label for creation date"
|
||||||
@@ -72,14 +72,26 @@
|
|||||||
"description": "Confirmation dialog for deleting all data"
|
"description": "Confirmation dialog for deleting all data"
|
||||||
},
|
},
|
||||||
"@delete_group": {
|
"@delete_group": {
|
||||||
"description": "Button text to delete a group"
|
"description": "Confirmation dialog for deleting a group"
|
||||||
|
},
|
||||||
|
"@delete_match": {
|
||||||
|
"description": "Button text to delete a match"
|
||||||
},
|
},
|
||||||
"@edit_group": {
|
"@edit_group": {
|
||||||
"description": "Button text to edit a group"
|
"description": "Button & Appbar label for editing a group"
|
||||||
|
},
|
||||||
|
"@enter_results": {
|
||||||
|
"description": "Button text to enter match results"
|
||||||
},
|
},
|
||||||
"@error_creating_group": {
|
"@error_creating_group": {
|
||||||
"description": "Error message when group creation fails"
|
"description": "Error message when group creation fails"
|
||||||
},
|
},
|
||||||
|
"@error_deleting_group": {
|
||||||
|
"description": "Error message when group deletion fails"
|
||||||
|
},
|
||||||
|
"@error_editing_group": {
|
||||||
|
"description": "Error message when group editing fails"
|
||||||
|
},
|
||||||
"@error_reading_file": {
|
"@error_reading_file": {
|
||||||
"description": "Error message when file cannot be read"
|
"description": "Error message when file cannot be read"
|
||||||
},
|
},
|
||||||
@@ -143,6 +155,9 @@
|
|||||||
"@match_name": {
|
"@match_name": {
|
||||||
"description": "Placeholder for match name input"
|
"description": "Placeholder for match name input"
|
||||||
},
|
},
|
||||||
|
"@match_profile": {
|
||||||
|
"description": "Title for match profile view"
|
||||||
|
},
|
||||||
"@matches": {
|
"@matches": {
|
||||||
"description": "Label for matches"
|
"description": "Label for matches"
|
||||||
},
|
},
|
||||||
@@ -179,6 +194,9 @@
|
|||||||
"@no_recent_matches_available": {
|
"@no_recent_matches_available": {
|
||||||
"description": "Message when no recent matches exist"
|
"description": "Message when no recent matches exist"
|
||||||
},
|
},
|
||||||
|
"@no_results_entered_yet": {
|
||||||
|
"description": "Message when no results have been entered yet"
|
||||||
|
},
|
||||||
"@no_second_match_available": {
|
"@no_second_match_available": {
|
||||||
"description": "Message when no second match exists"
|
"description": "Message when no second match exists"
|
||||||
},
|
},
|
||||||
@@ -220,6 +238,9 @@
|
|||||||
"@recent_matches": {
|
"@recent_matches": {
|
||||||
"description": "Title for recent matches section"
|
"description": "Title for recent matches section"
|
||||||
},
|
},
|
||||||
|
"@results": {
|
||||||
|
"description": "Label for match results"
|
||||||
|
},
|
||||||
"@ruleset": {
|
"@ruleset": {
|
||||||
"description": "Ruleset label"
|
"description": "Ruleset label"
|
||||||
},
|
},
|
||||||
@@ -235,6 +256,9 @@
|
|||||||
"@ruleset_single_winner": {
|
"@ruleset_single_winner": {
|
||||||
"description": "Description for single winner ruleset"
|
"description": "Description for single winner ruleset"
|
||||||
},
|
},
|
||||||
|
"@save_changes": {
|
||||||
|
"description": "Save changes button text"
|
||||||
|
},
|
||||||
"@search_for_groups": {
|
"@search_for_groups": {
|
||||||
"description": "Hint text for group search input field"
|
"description": "Hint text for group search input field"
|
||||||
},
|
},
|
||||||
@@ -321,8 +345,12 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"delete_all_data": "Delete all data",
|
"delete_all_data": "Delete all data",
|
||||||
"delete_group": "Delete Group",
|
"delete_group": "Delete Group",
|
||||||
|
"delete_match": "Delete Match",
|
||||||
"edit_group": "Edit Group",
|
"edit_group": "Edit Group",
|
||||||
|
"enter_results": "Enter Results",
|
||||||
"error_creating_group": "Error while creating group, please try again",
|
"error_creating_group": "Error while creating group, please try again",
|
||||||
|
"error_deleting_group": "Error while deleting group, please try again",
|
||||||
|
"error_editing_group": "Error while editing group, please try again",
|
||||||
"error_reading_file": "Error reading file",
|
"error_reading_file": "Error reading file",
|
||||||
"export_canceled": "Export canceled",
|
"export_canceled": "Export canceled",
|
||||||
"export_data": "Export data",
|
"export_data": "Export data",
|
||||||
@@ -344,6 +372,7 @@
|
|||||||
"licenses": "Licenses",
|
"licenses": "Licenses",
|
||||||
"match_in_progress": "Match in progress...",
|
"match_in_progress": "Match in progress...",
|
||||||
"match_name": "Match name",
|
"match_name": "Match name",
|
||||||
|
"match_profile": "Match Profile",
|
||||||
"matches": "Matches",
|
"matches": "Matches",
|
||||||
"members": "Members",
|
"members": "Members",
|
||||||
"most_points": "Most Points",
|
"most_points": "Most Points",
|
||||||
@@ -356,6 +385,7 @@
|
|||||||
"no_players_found_with_that_name": "No players found with that name",
|
"no_players_found_with_that_name": "No players found with that name",
|
||||||
"no_players_selected": "No players selected",
|
"no_players_selected": "No players selected",
|
||||||
"no_recent_matches_available": "No recent matches available",
|
"no_recent_matches_available": "No recent matches available",
|
||||||
|
"no_results_entered_yet": "No results entered yet",
|
||||||
"no_second_match_available": "No second match available",
|
"no_second_match_available": "No second match available",
|
||||||
"no_statistics_available": "No statistics available",
|
"no_statistics_available": "No statistics available",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
@@ -368,11 +398,13 @@
|
|||||||
"privacy_policy": "Privacy Policy",
|
"privacy_policy": "Privacy Policy",
|
||||||
"quick_create": "Quick Create",
|
"quick_create": "Quick Create",
|
||||||
"recent_matches": "Recent Matches",
|
"recent_matches": "Recent Matches",
|
||||||
|
"results": "Results",
|
||||||
"ruleset": "Ruleset",
|
"ruleset": "Ruleset",
|
||||||
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.",
|
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.",
|
||||||
"ruleset_most_points": "Traditional ruleset: the player with the most points wins.",
|
"ruleset_most_points": "Traditional ruleset: the player with the most points wins.",
|
||||||
"ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.",
|
"ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.",
|
||||||
"ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
|
"ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
|
||||||
|
"save_changes": "Save Changes",
|
||||||
"search_for_groups": "Search for groups",
|
"search_for_groups": "Search for groups",
|
||||||
"search_for_players": "Search for players",
|
"search_for_players": "Search for players",
|
||||||
"select_winner": "Select Winner:",
|
"select_winner": "Select Winner:",
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ abstract class AppLocalizations {
|
|||||||
/// **'Create match'**
|
/// **'Create match'**
|
||||||
String get create_match;
|
String get create_match;
|
||||||
|
|
||||||
/// Button text to create a new group
|
/// Appbar text to create a new group
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Create new group'**
|
/// **'Create new group'**
|
||||||
@@ -182,7 +182,7 @@ abstract class AppLocalizations {
|
|||||||
/// **'Created on'**
|
/// **'Created on'**
|
||||||
String get created_on;
|
String get created_on;
|
||||||
|
|
||||||
/// Button text to create a new match
|
/// Appbar text to create a new match
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Create new match'**
|
/// **'Create new match'**
|
||||||
@@ -230,24 +230,48 @@ abstract class AppLocalizations {
|
|||||||
/// **'Delete all data'**
|
/// **'Delete all data'**
|
||||||
String get delete_all_data;
|
String get delete_all_data;
|
||||||
|
|
||||||
/// Button text to delete a group
|
/// Confirmation dialog for deleting a group
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Delete Group'**
|
/// **'Delete Group'**
|
||||||
String get delete_group;
|
String get delete_group;
|
||||||
|
|
||||||
/// Button text to edit a group
|
/// Button text to delete a match
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Delete Match'**
|
||||||
|
String get delete_match;
|
||||||
|
|
||||||
|
/// Button & Appbar label for editing a group
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Edit Group'**
|
/// **'Edit Group'**
|
||||||
String get edit_group;
|
String get edit_group;
|
||||||
|
|
||||||
|
/// Button text to enter match results
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Enter Results'**
|
||||||
|
String get enter_results;
|
||||||
|
|
||||||
/// Error message when group creation fails
|
/// Error message when group creation fails
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Error while creating group, please try again'**
|
/// **'Error while creating group, please try again'**
|
||||||
String get error_creating_group;
|
String get error_creating_group;
|
||||||
|
|
||||||
|
/// Error message when group deletion fails
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Error while deleting group, please try again'**
|
||||||
|
String get error_deleting_group;
|
||||||
|
|
||||||
|
/// Error message when group editing fails
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Error while editing group, please try again'**
|
||||||
|
String get error_editing_group;
|
||||||
|
|
||||||
/// Error message when file cannot be read
|
/// Error message when file cannot be read
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -374,6 +398,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Match name'**
|
/// **'Match name'**
|
||||||
String get match_name;
|
String get match_name;
|
||||||
|
|
||||||
|
/// Title for match profile view
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Match Profile'**
|
||||||
|
String get match_profile;
|
||||||
|
|
||||||
/// Label for matches
|
/// Label for matches
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -446,6 +476,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'No recent matches available'**
|
/// **'No recent matches available'**
|
||||||
String get no_recent_matches_available;
|
String get no_recent_matches_available;
|
||||||
|
|
||||||
|
/// Message when no results have been entered yet
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'No results entered yet'**
|
||||||
|
String get no_results_entered_yet;
|
||||||
|
|
||||||
/// Message when no second match exists
|
/// Message when no second match exists
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -518,6 +554,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Recent Matches'**
|
/// **'Recent Matches'**
|
||||||
String get recent_matches;
|
String get recent_matches;
|
||||||
|
|
||||||
|
/// Label for match results
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Results'**
|
||||||
|
String get results;
|
||||||
|
|
||||||
/// Ruleset label
|
/// Ruleset label
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -548,6 +590,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'**
|
/// **'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.'**
|
||||||
String get ruleset_single_winner;
|
String get ruleset_single_winner;
|
||||||
|
|
||||||
|
/// Save changes button text
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Save Changes'**
|
||||||
|
String get save_changes;
|
||||||
|
|
||||||
/// Hint text for group search input field
|
/// Hint text for group search input field
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -81,13 +81,27 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get delete_group => 'Gruppe löschen';
|
String get delete_group => 'Gruppe löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get delete_match => 'Spiel löschen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get edit_group => 'Gruppe bearbeiten';
|
String get edit_group => 'Gruppe bearbeiten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get enter_results => 'Ergebnisse eintragen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get error_creating_group =>
|
String get error_creating_group =>
|
||||||
'Fehler beim Erstellen der Gruppe, bitte erneut versuchen';
|
'Fehler beim Erstellen der Gruppe, bitte erneut versuchen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get error_deleting_group =>
|
||||||
|
'Fehler beim Löschen der Gruppe, bitte erneut versuchen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get error_editing_group =>
|
||||||
|
'Fehler beim Bearbeiten der Gruppe, bitte erneut versuchen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get error_reading_file => 'Fehler beim Lesen der Datei';
|
String get error_reading_file => 'Fehler beim Lesen der Datei';
|
||||||
|
|
||||||
@@ -151,6 +165,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get match_name => 'Spieltitel';
|
String get match_name => 'Spieltitel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get match_profile => 'Spielprofil';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get matches => 'Spiele';
|
String get matches => 'Spiele';
|
||||||
|
|
||||||
@@ -188,6 +205,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get no_recent_matches_available => 'Keine letzten Spiele verfügbar';
|
String get no_recent_matches_available => 'Keine letzten Spiele verfügbar';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get no_results_entered_yet => 'Noch keine Ergebnisse eingetragen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get no_second_match_available => 'Kein zweites Spiel verfügbar';
|
String get no_second_match_available => 'Kein zweites Spiel verfügbar';
|
||||||
|
|
||||||
@@ -226,6 +246,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get recent_matches => 'Letzte Spiele';
|
String get recent_matches => 'Letzte Spiele';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get results => 'Ergebnisse';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ruleset => 'Regelwerk';
|
String get ruleset => 'Regelwerk';
|
||||||
|
|
||||||
@@ -245,6 +268,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get ruleset_single_winner =>
|
String get ruleset_single_winner =>
|
||||||
'Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.';
|
'Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get save_changes => 'Änderungen speichern';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get search_for_groups => 'Nach Gruppen suchen';
|
String get search_for_groups => 'Nach Gruppen suchen';
|
||||||
|
|
||||||
|
|||||||
@@ -81,13 +81,27 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get delete_group => 'Delete Group';
|
String get delete_group => 'Delete Group';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get delete_match => 'Delete Match';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get edit_group => 'Edit Group';
|
String get edit_group => 'Edit Group';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get enter_results => 'Enter Results';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get error_creating_group =>
|
String get error_creating_group =>
|
||||||
'Error while creating group, please try again';
|
'Error while creating group, please try again';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get error_deleting_group =>
|
||||||
|
'Error while deleting group, please try again';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get error_editing_group =>
|
||||||
|
'Error while editing group, please try again';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get error_reading_file => 'Error reading file';
|
String get error_reading_file => 'Error reading file';
|
||||||
|
|
||||||
@@ -151,6 +165,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get match_name => 'Match name';
|
String get match_name => 'Match name';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get match_profile => 'Match Profile';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get matches => 'Matches';
|
String get matches => 'Matches';
|
||||||
|
|
||||||
@@ -188,6 +205,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get no_recent_matches_available => 'No recent matches available';
|
String get no_recent_matches_available => 'No recent matches available';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get no_results_entered_yet => 'No results entered yet';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get no_second_match_available => 'No second match available';
|
String get no_second_match_available => 'No second match available';
|
||||||
|
|
||||||
@@ -226,6 +246,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get recent_matches => 'Recent Matches';
|
String get recent_matches => 'Recent Matches';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get results => 'Results';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ruleset => 'Ruleset';
|
String get ruleset => 'Ruleset';
|
||||||
|
|
||||||
@@ -245,6 +268,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get ruleset_single_winner =>
|
String get ruleset_single_winner =>
|
||||||
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.';
|
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get save_changes => 'Save Changes';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get search_for_groups => 'Search for groups';
|
String get search_for_groups => 'Search for groups';
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,22 @@ class GameTracker extends StatelessWidget {
|
|||||||
primaryColor: CustomTheme.primaryColor,
|
primaryColor: CustomTheme.primaryColor,
|
||||||
scaffoldBackgroundColor: CustomTheme.backgroundColor,
|
scaffoldBackgroundColor: CustomTheme.backgroundColor,
|
||||||
appBarTheme: CustomTheme.appBarTheme,
|
appBarTheme: CustomTheme.appBarTheme,
|
||||||
|
radioTheme: RadioThemeData(
|
||||||
|
fillColor: WidgetStateProperty.resolveWith<Color>((states) {
|
||||||
|
if (states.contains(WidgetState.selected)) {
|
||||||
|
return CustomTheme.primaryColor;
|
||||||
|
}
|
||||||
|
return CustomTheme.textColor;
|
||||||
|
}),
|
||||||
|
),
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
seedColor: CustomTheme.primaryColor,
|
seedColor: CustomTheme.primaryColor,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
).copyWith(surface: CustomTheme.backgroundColor),
|
primary: CustomTheme.primaryColor,
|
||||||
|
onPrimary: CustomTheme.textColor,
|
||||||
|
surface: CustomTheme.backgroundColor,
|
||||||
|
onSurface: CustomTheme.textColor,
|
||||||
|
),
|
||||||
pageTransitionsTheme: const PageTransitionsTheme(
|
pageTransitionsTheme: const PageTransitionsTheme(
|
||||||
builders: {
|
builders: {
|
||||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:tallee/core/adaptive_page_route.dart';
|
import 'package:tallee/core/adaptive_page_route.dart';
|
||||||
import 'package:tallee/core/custom_theme.dart';
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/group_view/groups_view.dart';
|
import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/home_view.dart';
|
import 'package:tallee/presentation/views/main_menu/home_view.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart';
|
import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart';
|
import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart';
|
||||||
@@ -38,7 +38,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
|
|||||||
),
|
),
|
||||||
KeyedSubtree(
|
KeyedSubtree(
|
||||||
key: ValueKey('groups_$tabKeyCount'),
|
key: ValueKey('groups_$tabKeyCount'),
|
||||||
child: const GroupsView(),
|
child: const GroupView(),
|
||||||
),
|
),
|
||||||
KeyedSubtree(
|
KeyedSubtree(
|
||||||
key: ValueKey('stats_$tabKeyCount'),
|
key: ValueKey('stats_$tabKeyCount'),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tallee/core/constants.dart';
|
|
||||||
import 'package:tallee/core/custom_theme.dart';
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
import 'package:tallee/core/enums.dart';
|
import 'package:tallee/core/enums.dart';
|
||||||
import 'package:tallee/data/db/database.dart';
|
import 'package:tallee/data/db/database.dart';
|
||||||
@@ -12,8 +11,10 @@ import 'package:tallee/presentation/widgets/player_selection.dart';
|
|||||||
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
|
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
|
||||||
|
|
||||||
class CreateGroupView extends StatefulWidget {
|
class CreateGroupView extends StatefulWidget {
|
||||||
/// A view that allows the user to create a new group
|
const CreateGroupView({super.key, this.groupToEdit});
|
||||||
const CreateGroupView({super.key});
|
|
||||||
|
/// The group to edit, if any
|
||||||
|
final Group? groupToEdit;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CreateGroupView> createState() => _CreateGroupViewState();
|
State<CreateGroupView> createState() => _CreateGroupViewState();
|
||||||
@@ -22,16 +23,29 @@ class CreateGroupView extends StatefulWidget {
|
|||||||
class _CreateGroupViewState extends State<CreateGroupView> {
|
class _CreateGroupViewState extends State<CreateGroupView> {
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
|
||||||
|
/// GlobalKey for ScaffoldMessenger to show snackbars
|
||||||
|
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||||
|
|
||||||
/// Controller for the group name input field
|
/// Controller for the group name input field
|
||||||
final _groupNameController = TextEditingController();
|
final _groupNameController = TextEditingController();
|
||||||
|
|
||||||
/// List of currently selected players
|
/// List of currently selected players
|
||||||
List<Player> selectedPlayers = [];
|
List<Player> selectedPlayers = [];
|
||||||
|
|
||||||
|
/// List of initially selected players (when editing a group)
|
||||||
|
List<Player> initialSelectedPlayers = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
if (widget.groupToEdit != null) {
|
||||||
|
_groupNameController.text = widget.groupToEdit!.name;
|
||||||
|
setState(() {
|
||||||
|
initialSelectedPlayers = widget.groupToEdit!.members;
|
||||||
|
selectedPlayers = widget.groupToEdit!.members;
|
||||||
|
});
|
||||||
|
}
|
||||||
_groupNameController.addListener(() {
|
_groupNameController.addListener(() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
@@ -47,10 +61,58 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final loc = AppLocalizations.of(context);
|
final loc = AppLocalizations.of(context);
|
||||||
return ScaffoldMessenger(
|
return ScaffoldMessenger(
|
||||||
|
key: _scaffoldMessengerKey,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
backgroundColor: CustomTheme.backgroundColor,
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
appBar: AppBar(title: Text(loc.create_new_group)),
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
widget.groupToEdit == null ? loc.create_new_group : loc.edit_group,
|
||||||
|
),
|
||||||
|
actions: widget.groupToEdit == null
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () async {
|
||||||
|
if (widget.groupToEdit != null) {
|
||||||
|
showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text(loc.delete_group),
|
||||||
|
content: Text(loc.this_cannot_be_undone),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(context).pop(false),
|
||||||
|
child: Text(loc.cancel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(context).pop(true),
|
||||||
|
child: Text(loc.delete),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).then((confirmed) async {
|
||||||
|
if (confirmed == true && context.mounted) {
|
||||||
|
bool success = await db.groupDao.deleteGroup(
|
||||||
|
groupId: widget.groupToEdit!.id,
|
||||||
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
if (success) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
} else {
|
||||||
|
if (!mounted) return;
|
||||||
|
showSnackbar(message: loc.error_deleting_group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@@ -60,11 +122,11 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
child: TextInputField(
|
child: TextInputField(
|
||||||
controller: _groupNameController,
|
controller: _groupNameController,
|
||||||
hintText: loc.group_name,
|
hintText: loc.group_name,
|
||||||
maxLength: Constants.MAX_GROUP_NAME_LENGTH,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PlayerSelection(
|
child: PlayerSelection(
|
||||||
|
initialSelectedPlayers: initialSelectedPlayers,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedPlayers = [...value];
|
selectedPlayers = [...value];
|
||||||
@@ -73,7 +135,9 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
CustomWidthButton(
|
CustomWidthButton(
|
||||||
text: loc.create_group,
|
text: widget.groupToEdit == null
|
||||||
|
? loc.create_group
|
||||||
|
: loc.edit_group,
|
||||||
sizeRelativeToWidth: 0.95,
|
sizeRelativeToWidth: 0.95,
|
||||||
buttonType: ButtonType.primary,
|
buttonType: ButtonType.primary,
|
||||||
onPressed:
|
onPressed:
|
||||||
@@ -81,28 +145,37 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
(selectedPlayers.length < 2))
|
(selectedPlayers.length < 2))
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
bool success = await db.groupDao.addGroup(
|
late Group? updatedGroup;
|
||||||
|
late bool success;
|
||||||
|
if (widget.groupToEdit == null) {
|
||||||
|
success = await db.groupDao.addGroup(
|
||||||
group: Group(
|
group: Group(
|
||||||
name: _groupNameController.text.trim(),
|
name: _groupNameController.text.trim(),
|
||||||
members: selectedPlayers,
|
members: selectedPlayers,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
updatedGroup = Group(
|
||||||
|
id: widget.groupToEdit!.id,
|
||||||
|
name: _groupNameController.text.trim(),
|
||||||
|
members: selectedPlayers,
|
||||||
|
);
|
||||||
|
//TODO: Implement group editing in database
|
||||||
|
/*
|
||||||
|
success = await db.groupDao.updateGroup(
|
||||||
|
group: updatedGroup,
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
if (success) {
|
if (success) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context, updatedGroup);
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
showSnackbar(
|
||||||
SnackBar(
|
message: widget.groupToEdit == null
|
||||||
backgroundColor: CustomTheme.boxColor,
|
? loc.error_creating_group
|
||||||
content: Center(
|
: loc.error_editing_group,
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(
|
|
||||||
context,
|
|
||||||
).error_creating_group,
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -114,4 +187,20 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Displays a snackbar with the given message and optional action.
|
||||||
|
///
|
||||||
|
/// [message] The message to display in the snackbar.
|
||||||
|
void showSnackbar({required String message}) {
|
||||||
|
final messenger = _scaffoldMessengerKey.currentState;
|
||||||
|
if (messenger != null) {
|
||||||
|
messenger.hideCurrentSnackBar();
|
||||||
|
messenger.showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(message, style: const TextStyle(color: Colors.white)),
|
||||||
|
backgroundColor: CustomTheme.boxColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:tallee/core/adaptive_page_route.dart';
|
||||||
import 'package:tallee/core/custom_theme.dart';
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
import 'package:tallee/data/db/database.dart';
|
import 'package:tallee/data/db/database.dart';
|
||||||
import 'package:tallee/data/dto/group.dart';
|
import 'package:tallee/data/dto/group.dart';
|
||||||
import 'package:tallee/data/dto/match.dart';
|
import 'package:tallee/data/dto/match.dart';
|
||||||
import 'package:tallee/data/dto/player.dart';
|
import 'package:tallee/data/dto/player.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
|
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
|
||||||
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
||||||
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
|
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
|
||||||
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||||
@@ -15,10 +17,10 @@ import 'package:tallee/presentation/widgets/custom_alert_dialog.dart';
|
|||||||
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||||
|
|
||||||
class GroupProfileView extends StatefulWidget {
|
class GroupDetailView extends StatefulWidget {
|
||||||
/// A view that displays the profile of a group
|
/// A view that displays the profile of a group
|
||||||
/// - [group]: The group to display
|
/// - [group]: The group to display
|
||||||
const GroupProfileView({
|
const GroupDetailView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.group,
|
required this.group,
|
||||||
required this.callback,
|
required this.callback,
|
||||||
@@ -30,21 +32,24 @@ class GroupProfileView extends StatefulWidget {
|
|||||||
final VoidCallback callback;
|
final VoidCallback callback;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GroupProfileView> createState() => _GroupProfileViewState();
|
State<GroupDetailView> createState() => _GroupDetailViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GroupProfileViewState extends State<GroupProfileView> {
|
class _GroupDetailViewState extends State<GroupDetailView> {
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
late Group _group;
|
||||||
|
|
||||||
/// Total matches played in this group
|
/// Total matches played in this group
|
||||||
int totalMatches = 0;
|
int totalMatches = 0;
|
||||||
|
|
||||||
|
/// The best player in this group
|
||||||
String bestPlayer = '';
|
String bestPlayer = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_group = widget.group;
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
_loadStatistics();
|
_loadStatistics();
|
||||||
}
|
}
|
||||||
@@ -87,7 +92,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
),
|
),
|
||||||
).then((confirmed) async {
|
).then((confirmed) async {
|
||||||
if (confirmed! && context.mounted) {
|
if (confirmed! && context.mounted) {
|
||||||
await db.groupDao.deleteGroup(groupId: widget.group.id);
|
await db.groupDao.deleteGroup(groupId: _group.id);
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
widget.callback.call();
|
widget.callback.call();
|
||||||
@@ -118,7 +123,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
widget.group.name,
|
_group.name,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -128,7 +133,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
Text(
|
Text(
|
||||||
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.group.createdAt)}',
|
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_group.createdAt)}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: CustomTheme.textColor,
|
color: CustomTheme.textColor,
|
||||||
@@ -145,7 +150,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
spacing: 12,
|
spacing: 12,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: widget.group.members.map((member) {
|
children: _group.members.map((member) {
|
||||||
return TextIconTile(
|
return TextIconTile(
|
||||||
text: member.name,
|
text: member.name,
|
||||||
iconEnabled: false,
|
iconEnabled: false,
|
||||||
@@ -163,7 +168,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
children: [
|
children: [
|
||||||
_buildStatRow(
|
_buildStatRow(
|
||||||
loc.members,
|
loc.members,
|
||||||
widget.group.members.length.toString(),
|
_group.members.length.toString(),
|
||||||
),
|
),
|
||||||
_buildStatRow(
|
_buildStatRow(
|
||||||
loc.played_matches,
|
loc.played_matches,
|
||||||
@@ -181,19 +186,22 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
child: MainMenuButton(
|
child: MainMenuButton(
|
||||||
text: loc.edit_group,
|
text: loc.edit_group,
|
||||||
icon: Icons.edit,
|
icon: Icons.edit,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
// TODO: Uncomment when GroupDetailView is implemented
|
final updatedGroup = await Navigator.push<Group?>(
|
||||||
/*
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
context,
|
||||||
adaptivePageRoute(
|
adaptivePageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
return CreateGroupView(groupToEdit: _group);
|
||||||
return const GroupDetailView();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);*/
|
);
|
||||||
print('Edit Group pressed');
|
if (updatedGroup != null && mounted) {
|
||||||
|
setState(() {
|
||||||
|
_group = updatedGroup;
|
||||||
|
});
|
||||||
|
_loadStatistics();
|
||||||
|
widget.callback();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -236,7 +244,7 @@ class _GroupProfileViewState extends State<GroupProfileView> {
|
|||||||
Future<void> _loadStatistics() async {
|
Future<void> _loadStatistics() async {
|
||||||
final matches = await db.matchDao.getAllMatches();
|
final matches = await db.matchDao.getAllMatches();
|
||||||
final groupMatches = matches
|
final groupMatches = matches
|
||||||
.where((match) => match.group?.id == widget.group.id)
|
.where((match) => match.group?.id == _group.id)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -8,21 +8,21 @@ import 'package:tallee/data/dto/group.dart';
|
|||||||
import 'package:tallee/data/dto/player.dart';
|
import 'package:tallee/data/dto/player.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
|
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/group_view/group_profile_view.dart';
|
import 'package:tallee/presentation/views/main_menu/group_view/group_detail_view.dart';
|
||||||
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
||||||
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/top_centered_message.dart';
|
import 'package:tallee/presentation/widgets/top_centered_message.dart';
|
||||||
|
|
||||||
class GroupsView extends StatefulWidget {
|
class GroupView extends StatefulWidget {
|
||||||
/// A view that displays a list of groups
|
/// A view that displays a list of groups
|
||||||
const GroupsView({super.key});
|
const GroupView({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GroupsView> createState() => _GroupsViewState();
|
State<GroupView> createState() => _GroupViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GroupsViewState extends State<GroupsView> {
|
class _GroupViewState extends State<GroupView> {
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
|
||||||
/// Loaded groups from the database
|
/// Loaded groups from the database
|
||||||
@@ -82,7 +82,7 @@ class _GroupsViewState extends State<GroupsView> {
|
|||||||
context,
|
context,
|
||||||
adaptivePageRoute(
|
adaptivePageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return GroupProfileView(
|
return GroupDetailView(
|
||||||
group: groups[index],
|
group: groups[index],
|
||||||
callback: loadGroups,
|
callback: loadGroups,
|
||||||
);
|
);
|
||||||
@@ -20,11 +20,14 @@ import 'package:tallee/presentation/widgets/tiles/choose_tile.dart';
|
|||||||
class CreateMatchView extends StatefulWidget {
|
class CreateMatchView extends StatefulWidget {
|
||||||
/// A view that allows creating a new match
|
/// A view that allows creating a new match
|
||||||
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
|
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
|
||||||
const CreateMatchView({super.key, this.onWinnerChanged});
|
const CreateMatchView({super.key, this.onWinnerChanged, this.match});
|
||||||
|
|
||||||
/// Optional callback invoked when the winner is changed
|
/// Optional callback invoked when the winner is changed
|
||||||
final VoidCallback? onWinnerChanged;
|
final VoidCallback? onWinnerChanged;
|
||||||
|
|
||||||
|
/// An optional match to prefill the fields
|
||||||
|
final Match? match;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CreateMatchView> createState() => _CreateMatchViewState();
|
State<CreateMatchView> createState() => _CreateMatchViewState();
|
||||||
}
|
}
|
||||||
@@ -64,6 +67,9 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
/// The currently selected players
|
/// The currently selected players
|
||||||
List<Player>? selectedPlayers;
|
List<Player>? selectedPlayers;
|
||||||
|
|
||||||
|
/// GlobalKey for ScaffoldMessenger to show snackbars
|
||||||
|
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -83,6 +89,20 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
filteredPlayerList = List.from(playerList);
|
filteredPlayerList = List.from(playerList);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If a match is provided, prefill the fields
|
||||||
|
if (widget.match != null) {
|
||||||
|
final match = widget.match!;
|
||||||
|
_matchNameController.text = match.name;
|
||||||
|
selectedGroup = match.group;
|
||||||
|
selectedGroupId = match.group?.id ?? '';
|
||||||
|
selectedPlayers = match.players ?? [];
|
||||||
|
if (selectedGroup != null) {
|
||||||
|
filteredPlayerList = playerList
|
||||||
|
.where((p) => !selectedGroup!.members.any((m) => m.id == p.id))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -106,7 +126,12 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final loc = AppLocalizations.of(context);
|
final loc = AppLocalizations.of(context);
|
||||||
|
final buttonText = widget.match != null
|
||||||
|
? loc.save_changes
|
||||||
|
: loc.create_match;
|
||||||
|
|
||||||
return ScaffoldMessenger(
|
return ScaffoldMessenger(
|
||||||
|
key: _scaffoldMessengerKey,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
backgroundColor: CustomTheme.backgroundColor,
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
@@ -187,11 +212,37 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
CustomWidthButton(
|
CustomWidthButton(
|
||||||
text: loc.create_match,
|
text: buttonText,
|
||||||
sizeRelativeToWidth: 0.95,
|
sizeRelativeToWidth: 0.95,
|
||||||
buttonType: ButtonType.primary,
|
buttonType: ButtonType.primary,
|
||||||
onPressed: _enableCreateGameButton()
|
onPressed: _enableCreateGameButton()
|
||||||
? () async {
|
? () {
|
||||||
|
buttonNavigation(context);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether the "Create Match" button should be enabled.
|
||||||
|
///
|
||||||
|
/// Returns `true` if:
|
||||||
|
/// - A ruleset is selected AND
|
||||||
|
/// - Either a group is selected OR at least 2 players are selected
|
||||||
|
bool _enableCreateGameButton() {
|
||||||
|
return (selectedGroup != null ||
|
||||||
|
(selectedPlayers != null && selectedPlayers!.length > 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonNavigation(BuildContext context) async {
|
||||||
|
if (widget.match != null) {
|
||||||
|
// TODO: Implement updating match logic here
|
||||||
|
Navigator.pop(context);
|
||||||
|
} else {
|
||||||
Match match = Match(
|
Match match = Match(
|
||||||
name: _matchNameController.text.isEmpty
|
name: _matchNameController.text.isEmpty
|
||||||
? (hintText ?? '')
|
? (hintText ?? '')
|
||||||
@@ -214,22 +265,5 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines whether the "Create Match" button should be enabled.
|
|
||||||
///
|
|
||||||
/// Returns `true` if:
|
|
||||||
/// - A ruleset is selected AND
|
|
||||||
/// - Either a group is selected OR at least 2 players are selected
|
|
||||||
bool _enableCreateGameButton() {
|
|
||||||
return (selectedGroup != null ||
|
|
||||||
(selectedPlayers != null && selectedPlayers!.length > 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,285 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:tallee/core/adaptive_page_route.dart';
|
||||||
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/dto/match.dart';
|
||||||
|
import 'package:tallee/data/dto/player.dart';
|
||||||
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
|
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
|
||||||
|
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/custom_alert_dialog.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||||
|
|
||||||
|
class MatchDetailView extends StatefulWidget {
|
||||||
|
/// A view that displays the profile of a match
|
||||||
|
/// - [match]: The match to display
|
||||||
|
/// - [callback]: Callback to refresh the match list
|
||||||
|
const MatchDetailView({
|
||||||
|
super.key,
|
||||||
|
required this.match,
|
||||||
|
required this.callback,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The match to display
|
||||||
|
final Match match;
|
||||||
|
|
||||||
|
/// Callback to refresh the match list
|
||||||
|
final VoidCallback callback;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MatchDetailView> createState() => _MatchDetailViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MatchDetailViewState extends State<MatchDetailView> {
|
||||||
|
late final AppDatabase db;
|
||||||
|
|
||||||
|
late Player? currentWinner;
|
||||||
|
|
||||||
|
/// All players who participated in the match
|
||||||
|
late final List<Player> allPlayers;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
allPlayers = _getAllPlayers();
|
||||||
|
currentWinner = widget.match.winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final loc = AppLocalizations.of(context);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(loc.match_profile),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () async {
|
||||||
|
showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CustomAlertDialog(
|
||||||
|
title: '${loc.delete_match}?',
|
||||||
|
content: loc.this_cannot_be_undone,
|
||||||
|
actions: [
|
||||||
|
AnimatedDialogButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: Text(
|
||||||
|
loc.cancel,
|
||||||
|
style: const TextStyle(color: CustomTheme.textColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AnimatedDialogButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: Text(
|
||||||
|
loc.delete,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: CustomTheme.secondaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).then((confirmed) async {
|
||||||
|
if (confirmed! && context.mounted) {
|
||||||
|
await db.matchDao.deleteMatch(matchId: widget.match.id);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
Navigator.pop(context);
|
||||||
|
widget.callback.call();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
top: 20,
|
||||||
|
bottom: 100,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
const Center(
|
||||||
|
child: ColoredIconContainer(
|
||||||
|
icon: Icons.sports_esports,
|
||||||
|
containerSize: 55,
|
||||||
|
iconSize: 38,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
widget.match.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.match.createdAt)}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
if (widget.match.group != null) ...[
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.group),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
// TODO: Update after DB changes
|
||||||
|
'${widget.match.group!.name} ${widget.match.players != null ? '+ ${widget.match.players!.length}' : ''}',
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
InfoTile(
|
||||||
|
title: loc.players,
|
||||||
|
icon: Icons.people,
|
||||||
|
horizontalAlignment: CrossAxisAlignment.start,
|
||||||
|
content: Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
spacing: 12,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: allPlayers.map((player) {
|
||||||
|
return TextIconTile(
|
||||||
|
text: player.name,
|
||||||
|
iconEnabled: false,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
InfoTile(
|
||||||
|
title: loc.results,
|
||||||
|
icon: Icons.emoji_events,
|
||||||
|
content: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 4,
|
||||||
|
horizontal: 8,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
/// TODO: Implement different ruleset results display
|
||||||
|
if (currentWinner != null) ...[
|
||||||
|
Text(
|
||||||
|
loc.winner,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
currentWinner!.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: widget.match.winner != null
|
||||||
|
? CustomTheme.primaryColor
|
||||||
|
: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
Text(
|
||||||
|
loc.no_results_entered_yet,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: MediaQuery.paddingOf(context).bottom,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
MainMenuButton(
|
||||||
|
icon: Icons.edit,
|
||||||
|
onPressed: () => Navigator.push(
|
||||||
|
context,
|
||||||
|
adaptivePageRoute(
|
||||||
|
fullscreenDialog: true,
|
||||||
|
builder: (context) =>
|
||||||
|
CreateMatchView(match: widget.match),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
MainMenuButton(
|
||||||
|
text: loc.enter_results,
|
||||||
|
icon: Icons.emoji_events,
|
||||||
|
onPressed: () async {
|
||||||
|
currentWinner = await Navigator.push(
|
||||||
|
context,
|
||||||
|
adaptivePageRoute(
|
||||||
|
fullscreenDialog: true,
|
||||||
|
builder: (context) => MatchResultView(
|
||||||
|
match: widget.match,
|
||||||
|
onWinnerChanged: () {
|
||||||
|
widget.callback.call();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all players who participated in the match (from group and individual players)
|
||||||
|
List<Player> _getAllPlayers() {
|
||||||
|
final List<Player> players = [];
|
||||||
|
|
||||||
|
// Add group members if group exists
|
||||||
|
if (widget.match.group != null) {
|
||||||
|
players.addAll(widget.match.group!.members);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add individual players
|
||||||
|
if (widget.match.players != null) {
|
||||||
|
for (var player in widget.match.players!) {
|
||||||
|
// Avoid duplicates
|
||||||
|
if (!players.any((p) => p.id == player.id)) {
|
||||||
|
players.add(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
widget.onWinnerChanged?.call();
|
widget.onWinnerChanged?.call();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop(_selectedPlayer);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(widget.match.name),
|
title: Text(widget.match.name),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import 'package:tallee/data/dto/match.dart';
|
|||||||
import 'package:tallee/data/dto/player.dart';
|
import 'package:tallee/data/dto/player.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
|
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
|
import 'package:tallee/presentation/views/main_menu/match_view/match_detail_view.dart';
|
||||||
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
||||||
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/match_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/match_tile.dart';
|
||||||
@@ -89,10 +89,9 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
adaptivePageRoute(
|
adaptivePageRoute(
|
||||||
fullscreenDialog: true,
|
builder: (context) => MatchDetailView(
|
||||||
builder: (context) => MatchResultView(
|
|
||||||
match: matches[index],
|
match: matches[index],
|
||||||
onWinnerChanged: loadGames,
|
callback: loadGames,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -128,6 +127,7 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
|
|
||||||
/// Loads the games from the database and sorts them by creation date.
|
/// Loads the games from the database and sorts them by creation date.
|
||||||
void loadGames() {
|
void loadGames() {
|
||||||
|
isLoading = true;
|
||||||
Future.wait([
|
Future.wait([
|
||||||
db.matchDao.getAllMatches(),
|
db.matchDao.getAllMatches(),
|
||||||
Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
|
Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class _SettingsViewState extends State<SettingsView> {
|
|||||||
version: 'n.A.',
|
version: 'n.A.',
|
||||||
buildNumber: 'n.A.',
|
buildNumber: 'n.A.',
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|||||||
@@ -2,25 +2,25 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class MainMenuButton extends StatefulWidget {
|
class MainMenuButton extends StatefulWidget {
|
||||||
/// A button for the main menu with an optional icon and a press animation.
|
/// A button for the main menu with an optional icon and a press animation.
|
||||||
/// - [text]: The text of the button.
|
|
||||||
/// - [icon]: The icon of the button.
|
|
||||||
/// - [onPressed]: The callback to be invoked when the button is pressed.
|
/// - [onPressed]: The callback to be invoked when the button is pressed.
|
||||||
|
/// - [icon]: The icon of the button.
|
||||||
|
/// - [text]: The text of the button.
|
||||||
const MainMenuButton({
|
const MainMenuButton({
|
||||||
super.key,
|
super.key,
|
||||||
required this.text,
|
|
||||||
this.icon,
|
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
|
required this.icon,
|
||||||
|
this.text,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The text of the button.
|
|
||||||
final String text;
|
|
||||||
|
|
||||||
/// The icon of the button.
|
|
||||||
final IconData? icon;
|
|
||||||
|
|
||||||
/// The callback to be invoked when the button is pressed.
|
/// The callback to be invoked when the button is pressed.
|
||||||
final void Function() onPressed;
|
final void Function() onPressed;
|
||||||
|
|
||||||
|
/// The icon of the button.
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
/// The text of the button.
|
||||||
|
final String? text;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MainMenuButton> createState() => _MainMenuButtonState();
|
State<MainMenuButton> createState() => _MainMenuButtonState();
|
||||||
}
|
}
|
||||||
@@ -71,12 +71,11 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (widget.icon != null) ...[
|
|
||||||
Icon(widget.icon, size: 26, color: Colors.black),
|
Icon(widget.icon, size: 26, color: Colors.black),
|
||||||
|
if (widget.text != null) ...[
|
||||||
const SizedBox(width: 7),
|
const SizedBox(width: 7),
|
||||||
],
|
|
||||||
Text(
|
Text(
|
||||||
widget.text,
|
widget.text!,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -84,6 +83,7 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
suggestedPlayers = skeletonData;
|
suggestedPlayers = skeletonData;
|
||||||
|
selectedPlayers = widget.initialSelectedPlayers ?? [];
|
||||||
loadPlayerList();
|
loadPlayerList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
// If the search is empty, it shows all unselected players.
|
// If the search is empty, it shows all unselected players.
|
||||||
suggestedPlayers = allPlayers.where((player) {
|
suggestedPlayers = allPlayers.where((player) {
|
||||||
return !selectedPlayers.contains(player);
|
return !selectedPlayers.any((p) => p.id == player.id);
|
||||||
}).toList();
|
}).toList();
|
||||||
} else {
|
} else {
|
||||||
// If there is input, it filters by name match (case-insensitive) and ensures
|
// If there is input, it filters by name match (case-insensitive) and ensures
|
||||||
@@ -109,9 +110,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
final bool nameMatches = player.name.toLowerCase().contains(
|
final bool nameMatches = player.name.toLowerCase().contains(
|
||||||
value.toLowerCase(),
|
value.toLowerCase(),
|
||||||
);
|
);
|
||||||
final bool isNotSelected = !selectedPlayers.contains(
|
final bool isNotSelected = !selectedPlayers.any((p) => p.id == player.id);
|
||||||
player,
|
|
||||||
);
|
|
||||||
return nameMatches && isNotSelected;
|
return nameMatches && isNotSelected;
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
@@ -126,6 +125,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
|
child: AppSkeleton(
|
||||||
|
enabled: isLoading,
|
||||||
child: selectedPlayers.isEmpty
|
child: selectedPlayers.isEmpty
|
||||||
? Center(child: Text(loc.no_players_selected))
|
? Center(child: Text(loc.no_players_selected))
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
@@ -167,6 +168,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
loc.all_players,
|
loc.all_players,
|
||||||
@@ -245,8 +247,22 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
// Otherwise, use the loaded players from the database.
|
// Otherwise, use the loaded players from the database.
|
||||||
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
||||||
allPlayers = [...loadedPlayers];
|
allPlayers = [...loadedPlayers];
|
||||||
|
if (widget.initialSelectedPlayers != null) {
|
||||||
|
// Excludes already selected players from the suggested players list.
|
||||||
|
suggestedPlayers = loadedPlayers.where((p) => !widget.initialSelectedPlayers!.any((ip) => ip.id == p.id)).toList();
|
||||||
|
// Ensures that only players available for selection are pre-selected.
|
||||||
|
selectedPlayers = widget.initialSelectedPlayers!
|
||||||
|
.where(
|
||||||
|
(p) => allPlayers.any(
|
||||||
|
(available) => available.id == p.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
// If no initial selection, all loaded players are suggested.
|
||||||
suggestedPlayers = [...loadedPlayers];
|
suggestedPlayers = [...loadedPlayers];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,11 +36,7 @@ class CustomRadioListTile<T> extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Radio<T>(
|
Radio<T>(value: value, toggleable: true),
|
||||||
value: value,
|
|
||||||
activeColor: CustomTheme.primaryColor,
|
|
||||||
toggleable: true,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
|
|||||||
Reference in New Issue
Block a user