diff --git a/.nicegui/storage-user-47e8cea6-65ad-4725-abd9-23cf84587a03.json b/.nicegui/storage-user-47e8cea6-65ad-4725-abd9-23cf84587a03.json new file mode 100644 index 0000000..c9a1563 --- /dev/null +++ b/.nicegui/storage-user-47e8cea6-65ad-4725-abd9-23cf84587a03.json @@ -0,0 +1 @@ +{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":1,"display_name":"Zorniger Klebschnüffler","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file diff --git a/database.py b/database.py index 180b221..6ff4c39 100644 --- a/database.py +++ b/database.py @@ -1,4 +1,5 @@ import sqlite3 +import random if __name__ == "__main__": @@ -16,8 +17,9 @@ def init_db(): CREATE TABLE IF NOT EXISTS players ( id INTEGER PRIMARY KEY AUTOINCREMENT, discord_id TEXT UNIQUE, - name TEXT NOT NULL, - avatar_url TEXT + discord_name TEXT NOT NULL, + discord_avatar_url TEXT, + display_name TEXT ) ''') @@ -63,12 +65,37 @@ def init_db(): ) ''') + # 5. Tabelle: achievements (Der globale Katalog aller möglichen Erfolge - Stammdaten) + cursor.execute(''' + CREATE TABLE IF NOT EXISTS achievements ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + description TEXT NOT NULL, + icon TEXT + ) + ''') + + # 6. Tabelle: player_achievements (Wer hat welchen Erfolg wann bekommen? - Bewegungsdaten) + cursor.execute(''' + CREATE TABLE IF NOT EXISTS player_achievements ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + player_id INTEGER, + achievement_id INTEGER, + earned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + earned_in INTEGER, + FOREIGN KEY (earned_in) REFERENCES gamesystems (id), + FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (achievement_id) REFERENCES achievements (id) + ) + ''') + connection.commit() connection.close() - print("Database structures created successfully!") - seed_gamesystems() print("Standard Daten in die DB Geschrieben.") +# -------------------- +# --- Start Setup der DB Daten. Damit sie nicht GANZ leer sind. +# -------------------- def seed_gamesystems(): connection = sqlite3.connect("warhammer_league.db") cursor = connection.cursor() @@ -85,26 +112,56 @@ def seed_gamesystems(): connection.close() print("Spielsysteme wurden angelegt!") - +def seed_achievements(): + connection = sqlite3.connect("warhammer_league.db") + cursor = connection.cursor() + + # Unsere Start-Stückliste an Erfolgen (Name, Beschreibung, Icon) + achievements = [ + ("Kingslayer", "Hat den Spieler auf Platz 1 besiegt!", "👑"), + ("Unstoppable", "3 Spiele in Folge gewonnen.", "🔥"), + ("First Blood", "Das allererste Ligaspiel absolviert.", "🩸") + ] + + cursor.executemany("INSERT INTO achievements (name, description, icon) VALUES (?, ?, ?)", achievements) + + connection.commit() + connection.close() + print("Achievements wurden in den Katalog geladen!") + + +def change_display_name(player_id, new_name): + connection = sqlite3.connect("warhammer_league.db") + cursor = connection.cursor() + + cursor.execute("UPDATE players SET display_name = ? WHERE id = ?", (new_name, player_id)) + + connection.commit() + connection.close() + def get_or_create_player(discord_id, discord_name, avatar_url): connection = sqlite3.connect("warhammer_league.db") cursor = connection.cursor() - # REPARIERT: Wir fragen nur noch id, name und avatar_url aus 'players' ab - cursor.execute("SELECT id, name, avatar_url FROM players WHERE discord_id = ?", (discord_id,)) + # REPARIERT: Wir fragen 4 Dinge ab (id, discord_name, display_name, discord_avatar_url) + cursor.execute("SELECT id, discord_name, display_name, discord_avatar_url FROM players WHERE discord_id = ?", (discord_id,)) player = cursor.fetchone() if player is None: - cursor.execute("INSERT INTO players (discord_id, name, avatar_url) VALUES (?, ?, ?)", (discord_id, discord_name, avatar_url)) + + silly_name = generate_silly_name() + cursor.execute("INSERT INTO players (discord_id, discord_name, display_name, discord_avatar_url) VALUES (?, ?, ?, ?)", (discord_id, discord_name, silly_name, avatar_url)) connection.commit() - cursor.execute("SELECT id, name, avatar_url FROM players WHERE discord_id = ?", (discord_id,)) + + cursor.execute("SELECT id, discord_name, display_name, discord_avatar_url FROM players WHERE discord_id = ?", (discord_id,)) player = cursor.fetchone() else: # Falls sich Name oder Bild auf Discord geändert haben, machen wir ein Update - cursor.execute("UPDATE players SET name = ?, avatar_url = ? WHERE discord_id = ?", (discord_name, avatar_url, discord_id)) + cursor.execute("UPDATE players SET discord_name = ?, discord_avatar_url = ? WHERE discord_id = ?", (discord_name, avatar_url, discord_id)) connection.commit() - cursor.execute("SELECT id, name, avatar_url FROM players WHERE discord_id = ?", (discord_id,)) + + cursor.execute("SELECT id, discord_name, display_name, discord_avatar_url FROM players WHERE discord_id = ?", (discord_id,)) player = cursor.fetchone() connection.close() @@ -127,4 +184,23 @@ def get_all_players(): return result +def generate_silly_name(): + adjectives = [ + "Verwirrter", "Blinder", "Heulender", "Zorniger", "Chaos", "" + "Verzweifelter", "Schreiender", "Stolpernder", "Schwitzender" + ] + nouns = [ + "Grot", "Kultist", "Servitor", "Snotling", + "Guardmen", "Würfellecker", "Regelvergesser", "Meta-Chaser", "Klebschnüffler" + ] + + # random.choice() nimmt zufällig ein Element aus der Liste + adj = random.choice(adjectives) + noun = random.choice(nouns) + + # Verbindet beide Wörter mit einem Leerzeichen (z.B. "Verwirrter Grot") + return f"{adj} {noun}" + + + diff --git a/gui/discord_login.py b/gui/discord_login.py index b5496bb..d1aad2a 100644 --- a/gui/discord_login.py +++ b/gui/discord_login.py @@ -62,7 +62,8 @@ def setup_login_routes(): app.storage.user['discord_id'] = discord_id app.storage.user['discord_name'] = discord_name app.storage.user['db_id'] = player[0] - app.storage.user['avatar_url'] = player[2] # Bild speichern! + app.storage.user['display_name'] = player[2] + app.storage.user['discord_avatar_url'] = player[3] # Bild speichern! ui.navigate.to('/') else: diff --git a/gui/gui_style.py b/gui/gui_style.py new file mode 100644 index 0000000..29f8b7b --- /dev/null +++ b/gui/gui_style.py @@ -0,0 +1,20 @@ +from nicegui import ui + +def apply_design(): + # 1. Dark Mode aktivieren + ui.dark_mode(True) + + # 2. Hier definierst du deine Farben fest im Code! + # Du kannst normale englische Farbnamen (wie 'red') oder HEX-Codes (wie '#ff0000') nutzen. + ui.colors( + primary='#b91c1c', # Hauptfarbe (z.B. für Standard-Buttons) - Hier ein dunkles Rot + secondary='#1f2937', # Zweitfarbe + accent='#3b82f6', # Akzentfarbe (z.B. Blau) + positive='#22c55e', # Farbe für Erfolg (Grün) + negative='#ef4444', # Farbe für Fehler/Abbruch (Rot) + info='#3b82f6', # Info-Farbe + warning='#f59e0b' # Warn-Farbe (Orange) + ) + + # (Optional) Wenn du die Hintergrundfarbe der ganzen Seite ändern willst: + # ui.query('body').style('background-color: #121212;') diff --git a/gui/main_gui.py b/gui/main_gui.py index 80218c1..dec04c0 100644 --- a/gui/main_gui.py +++ b/gui/main_gui.py @@ -1,37 +1,84 @@ from nicegui import ui, app -from gui import discord_login +import database +from gui import discord_login, gui_style def setup_routes(): @ui.page('/') def home_page(): - ui.dark_mode(True) + gui_style.apply_design() # --- NAVIGATIONSLEISTE (HEADER) --- with ui.header().classes('items-center justify-between bg-zinc-900 p-4 shadow-lg'): - ui.label('Diceghost Liga').classes('text-2xl font-bold text-white') + # --- 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 --- if app.storage.user.get('authenticated', False): with ui.row().classes('items-center gap-4'): - ui.label(app.storage.user.get('discord_name')).classes('text-lg text-gray-300') + discord_name = app.storage.user.get('discord_name') + display_name = app.storage.user.get('display_name') + db_id = app.storage.user.get('db_id') + + # 1. Wir definieren eine 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')), der exakt wie ein FAB aussieht! + 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-2') 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: + import database + database.change_display_name(db_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() + + 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='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-blue-500') + ui.image(avatar).classes('w-12 h-12 rounded-full border-2 border-red-500') - def logout(): - app.storage.user.clear() - ui.navigate.to('/') + def logout(): + app.storage.user.clear() + ui.navigate.to('/') + + ui.button('Logout', on_click=logout).classes('bg-red-500 text-white') - ui.button('Logout', on_click=logout).classes('mt-4 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)) + # ---------------------------------- + # --- Spielsysteme --- if app.storage.user.get('authenticated', False): with ui.card().classes("w-full"): - ui.button('Enter Match', on_click=lambda: ui.navigate.to('/add-match')).classes('mt-4 bg-blue-500 text-white') ui.label(text="Meine Ligaplätze").classes("font-bold text-white") with ui.row().classes("w-full h-30 gap-4 items-center"): #Einfügen: Eine UI Card PRO Spielsystem des Spielers. @@ -43,8 +90,6 @@ def setup_routes(): with ui.card().classes("flex-1 h-24 items-center justify-center cursor-pointer hover:bg-zinc-800 transition-colors").on('click', lambda: ui.navigate.to('/statistic/Spearhead')): ui.label(text="Spearhead").classes('text-xl font-bold') - - with ui.card().classes("w-full"): - ui.label(text="Meine Statistik").classes("font-bold text-white") + diff --git a/gui/match_gui.py b/gui/match_gui.py index 6c355dd..ebf1e23 100644 --- a/gui/match_gui.py +++ b/gui/match_gui.py @@ -1,11 +1,14 @@ from nicegui import ui, app +from gui import gui_style def setup_match_routes(): # Unsere neue Unterseite für das Eintragen der Spiele @ui.page('/add-match') def add_match_page(): - ui.dark_mode(True) + + gui_style.apply_design() + with ui.card().classes('w-full items-center mt-10'): ui.label('Enter New Match').classes('text-2xl font-bold') diff --git a/gui/pictures/wsdg.png b/gui/pictures/wsdg.png new file mode 100644 index 0000000..9620d5f Binary files /dev/null and b/gui/pictures/wsdg.png differ diff --git a/main.py b/main.py index af57ed3..624e0a2 100644 --- a/main.py +++ b/main.py @@ -21,6 +21,7 @@ if not os.path.exists(db_file): # 1. Erstellt die leere Datei und alle Tabellen-Strukturen database.init_db() database.seed_gamesystems() + database.seed_achievements() print("Datenbank erfolgreich aufgebaut!") else: