Liga-System/gui/main_gui.py

287 lines
15 KiB
Python
Raw Normal View History

from nicegui import ui, app
from data import database, data_api
from gui import discord_login, gui_style
from match_calculations import calc_match
def setup_routes(admin_discord_id):
@ui.page('/', dark=True)
def home_page():
gui_style.apply_design()
# ---------------------------
# --- NAVIGATIONSLEISTE (HEADER) ---
# ---------------------------
with ui.header().classes('items-center justify-between bg-zinc-900 p-4 shadow-lg'):
# --- LINKE SEITE ---
# Vereinslogo und den Titel in einer eigenen Reihe (Reihe 1)
with ui.row().classes('items-center gap-4'):
ui.image("gui/pictures/wsdg.png").classes('w-20 h-20 rounded-full')
ui.label('Diceghost Liga').classes('text-2xl font-bold text-white')
# --- RECHTE SEITE ---
discord_id = app.storage.user.get("discord_id")
if discord_id == admin_discord_id:
ui.button('Admin Panel', on_click=lambda: ui.navigate.to('/admin'))
2026-03-02 16:17:50 +01:00
# --- RECHTE SEITE ---
if app.storage.user.get('authenticated', False):
with ui.row().classes('items-center gap-4'):
discord_name = app.storage.user.get('discord_name')
display_name = app.storage.user.get('display_name')
2026-03-02 16:17:50 +01:00
player_id = app.storage.user.get('db_id')
# 1. kleine Funktion, die zwischen Text und Eingabe hin- und herschaltet
def toggle_edit_mode():
display_row.visible = not display_row.visible
edit_row.visible = not edit_row.visible
# --- ANSICHT 1: Der normale Text mit Edit-Button ---
with ui.row().classes('items-center gap-2') as display_row:
ui.label(display_name).classes('text-xl font-bold text-white')
ui.label('aka').classes('text-sm text-gray-500')
ui.label(discord_name).classes('text-lg text-gray-400')
# Ein runder Button (.props('round'))
ui.button(icon='edit', color='primary', on_click=toggle_edit_mode).props('round dense')
# --- ANSICHT 2: Das Eingabefeld (startet unsichtbar!) ---
with ui.row().classes('items-center gap-5') as edit_row:
edit_row.visible = False # Am Anfang verstecken
def save_new_name():
new_name = name_input.value
# Nur speichern, wenn ein Name drinsteht und er anders ist als vorher
if new_name and new_name != display_name:
print("save")
data_api.change_display_name(player_id, new_name) # Deine DB Funktion
app.storage.user['display_name'] = new_name
ui.notify('Name gespeichert!', color='positive')
ui.navigate.reload()
else:
# Wenn nichts geändert wurde, einfach wieder einklappen
toggle_edit_mode()
def generate_random_silly_name():
silly_name = data_api.generate_silly_name()
name_input.value=silly_name
name_input = ui.input('Neuer Name', value=display_name).on('keydown.enter', save_new_name)
ui.button(icon='save', color='positive', on_click=save_new_name).props('round dense')
ui.button(icon='casino', on_click=generate_random_silly_name).props('round dense')
ui.button(icon='close', color='negative', on_click=toggle_edit_mode).props('round dense')
avatar = app.storage.user.get('avatar_url')
if avatar:
ui.image(avatar).classes('w-12 h-12 rounded-full border-2 border-red-500')
def logout():
app.storage.user.clear()
ui.navigate.to('/')
ui.button('Logout', on_click=logout).classes('bg-red-500 text-white')
else:
auth_url = discord_login.get_auth_url()
ui.button('Login with Discord', on_click=lambda: ui.navigate.to(auth_url))
# ---------------------------
# --- Match Bestätigung ---
# ---------------------------
# --- Bestätigungs-Bereich für offene Spiele --- Der "Marian Balken !!!1!11!"
if app.storage.user.get('authenticated', False):
unconfirmed_matches = data_api.get_unconfirmed_matches(player_id)
if len(unconfirmed_matches) > 0:
# Eine auffällige, rote Karte über die volle Breite
with ui.card().classes('w-full bg-red-900/80 border-2 border-red-500 mb-6'):
ui.label(f"Aktion erforderlich: Du hast {len(unconfirmed_matches)} unbestätigte(s) Spiel(e)!").classes('text-2xl font-bold text-white mb-2')
for match in unconfirmed_matches:
# --- NEU: Die Funktion, die beim Klick ausgeführt wird ---
def reject_match(m_id):
data_api.delete_match(m_id)
ui.notify("Spiel abgelehnt und gelöscht!", color="warning")
ui.navigate.reload() # Lädt die Seite neu, um die Karte zu aktualisieren
def acccept_match(m_id):
ui.notify("Spiel akzeptiert. Wird Berechnet.")
ui.navigate.reload() # Lädt die Seite neu, um die Karte zu aktualisieren
calc_match.calculate_match(m_id)
# Für jedes Match machen wir eine kleine Reihe
with ui.row().classes('w-full items-center justify-between bg-zinc-900 p-3 rounded shadow-inner'):
# Info-Text: Was wurde eingetragen?
info_text = f"[{match['system_name']}] {match['p1_name']} behauptet: Er hat {match['score_player1']} : {match['score_player2']} gegen dich gespielt."
ui.label(info_text).classes('text-lg text-gray-200')
# Die Buttons (Funktion machen wir im nächsten Schritt!)
with ui.row().classes('gap-2'):
# ABLEHNEN und Spiel löschen
ui.button("Ablehnen", color="negative", icon="close", on_click=lambda e, m_id=match['match_id']: reject_match(m_id))
ui.space()
# BESTÄTIGEN und spiel berechnen lassen
ui.button("Bestätigen", color="positive", icon="check", on_click=lambda e, m_id=match['match_id']: acccept_match(m_id))
# ---------------------------
# --- Selbst eingetragene, offene Spiele ---
# ---------------------------
submitted_matches = data_api.get_submitted_matches(player_id)
if len(submitted_matches) > 0:
# Eine etwas dezentere Karte (grau)
with ui.card().classes('w-full bg-zinc-800 border border-gray-600 mb-6'):
ui.label(f"Warten auf Gegner: Du hast {len(submitted_matches)} offene(s) Spiel(e) eingetragen.").classes('text-xl font-bold text-gray-300 mb-2')
for match in submitted_matches:
# Die Lösch-Funktion, die beim Klick ausgeführt wird
def retract_match(m_id):
data_api.delete_match(m_id)
ui.notify("Eingetragenes Spiel zurückgezogen!", color="warning")
ui.navigate.reload()
# Für jedes Match machen wir eine kleine Reihe
with ui.row().classes('w-full items-center justify-between bg-zinc-900 p-3 rounded shadow-inner'):
# Info-Text: Auf wen warten wir?
info_text = f"[{match['system_name']}] Warten auf Bestätigung von {match['p2_name']} ({match['score_player1']} : {match['score_player2']})"
ui.label(info_text).classes('text-lg text-gray-400')
# Der Zurückziehen-Button (wieder mit unserem lambda m_id=... Trick!)
ui.button("Zurückziehen", color="warning", icon="delete", on_click=lambda e, m_id=match['match_id']: retract_match(m_id))
# ---------------------------
# --- Spielsysteme ---
# ---------------------------
2026-02-26 16:20:05 +01:00
if app.storage.user.get('authenticated', False):
with ui.card().classes("w-full"):
2026-03-02 16:17:50 +01:00
ui.label(text="Meine Ligaplätze").classes("font-bold text-white text-xl")
placements = data_api.get_player_statistics(player_id)
systems = data_api.get_gamesystem_data()
2026-03-02 16:17:50 +01:00
def click_join_league(p_id, sys_id):
data_api.join_league(p_id, sys_id)
ui.navigate.to("/")
# --- NEU: Wir machen die Funktion allgemein ---
def toggle_visibility(row_a, row_b):
row_a.visible = not row_a.visible
row_b.visible = not row_b.visible
with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-center"):
2026-03-02 16:17:50 +01:00
for place in placements:
sys_card = ui.card().classes("h-60 w-full items-center justify-center transition-colors")
with sys_card:
# Kopfzeile der Karte (Name & Beschreibung)
ui.label(text=place['gamesystem_name']).classes('text-xl font-bold')
if 'description' in place and place['description']:
ui.label(text=place['description']).classes('text-xs text-gray-400')
ui.space()
if place['mmr'] is None:
ui.label(text="Du bist noch nicht in dieser Liga.").classes("text-red-500 font-bold")
# 1. Wir legen die Reihen an und speichern sie in lokalen Variablen
join_row = ui.row().classes('items-center gap-2')
confirm_row = ui.row().classes('items-center gap-2')
confirm_row.visible = False # Standardmäßig unsichtbar
# 2. Erste Reihe (Der "Beitreten" Button)
with join_row:
ui.button("Beitreten", on_click=lambda e, r1=join_row, r2=confirm_row: toggle_visibility(r1, r2))
2026-03-02 16:17:50 +01:00
# 3. Zweite Reihe (Die Bestätigungs-Buttons)
with confirm_row:
ui.button("Liga Beitreten", color="green", on_click=lambda e, p=player_id, s=place['gamesystem_id']: click_join_league(p, s))
# Der Abbrechen Button kriegt den gleichen Toggle-Befehl wie oben:
ui.button(icon='cancel', color='red', on_click=lambda e, r1=join_row, r2=confirm_row: toggle_visibility(r1, r2)).props('round dense')
2026-03-02 16:17:50 +01:00
else:
# Spieler IST in der Liga!
sys_card.classes("cursor-pointer hover:bg-zinc-800")
# Auch hier machen wir es mit dem 'e' absolut sicher:
sys_card.on('click', lambda e, name=place['gamesystem_name']: ui.navigate.to(f'/statistic/{name}'))
# Wir zeigen die Stats.
with ui.row().classes('items-center gap-4'):
ui.label(text=f"MMR: {place['mmr']}")
ui.label(text=f"Spiele: {place['games_in_system']}")
# ---------------------------
# Match Historie
# ---------------------------
with ui.card().classes("w-full"):
ui.label(text= "Meine letzten Spiele").classes("font-bold text-white text-xl")
# 1. Daten aus der DB holen
raw_matches = data_api.get_recent_matches_for_player(player_id)
# 2. Daten für die Tabelle aufbereiten
table_rows = []
for match in raw_matches:
# Bin ich Spieler 1 oder Spieler 2?
if match['player1_id'] == player_id:
# Ich bin Spieler 1, also ist Spieler 2 der Gegner
opponent_name = f"{match['p2_display']} aka {match['p2_discord']}"
my_score = match['score_player1']
opp_score = match['score_player2']
else:
# Ich bin Spieler 2, also ist Spieler 1 der Gegner
opponent_name = f"{match['p1_display']} aka {match['p1_discord']}"
my_score = match['score_player2']
opp_score = match['score_player1']
# Gewonnen oder Verloren?
if my_score > opp_score:
result_text = "Gewonnen"
elif my_score < opp_score:
result_text = "Verloren"
else:
result_text = "Unentschieden"
# Datum hübsch machen (schneidet die Millisekunden weg)
date_clean = str(match['played_at'])[:10]
# Eine fertige Zeile für unsere Tabelle bauen
table_rows.append({
'date': date_clean,
'system': match['gamesystem_name'],
'opponent': opponent_name,
'result': f"{result_text} ({my_score} : {opp_score})"
})
# 3. Das Layout (Die Spalten) der Tabelle definieren
table_columns = [
{'name': 'date', 'label': 'Gespielt am', 'field': 'date', 'align': 'left'},
{'name': 'system', 'label': 'System', 'field': 'system', 'align': 'left'},
{'name': 'opponent', 'label': 'Gegner', 'field': 'opponent', 'align': 'left'},
{'name': 'result', 'label': 'Ergebnis', 'field': 'result', 'align': 'left'}
]
# 4. Die Tabelle zeichnen!
if len(table_rows) > 0:
ui.table(columns=table_columns, rows=table_rows, row_key='date').classes('w-full bg-zinc-900 text-white')
else:
ui.label("Noch keine Spiele absolviert.").classes("text-gray-500 italic")
2026-03-02 16:17:50 +01:00
2026-02-26 16:20:05 +01:00
2026-02-26 16:20:05 +01:00