Add archived screenshots documentation and migrate new assets to docs/80_Assets/. Include system mockups, tournament files, and dashboard visuals for Neumarkt 2026.
Some checks are pending
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Waiting to run
22
docs/06_Frontend/Screenshots/README.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
type: Guide
|
||||||
|
status: ARCHIVED
|
||||||
|
owner: Curator
|
||||||
|
last_update: 2026-04-09
|
||||||
|
---
|
||||||
|
|
||||||
|
# Screenshots (ARCHIVED)
|
||||||
|
|
||||||
|
Dieser Ordner ist archiviert. Alle aktuellen UI-Screenshots werden zentral unter
|
||||||
|
`docs/80_Assets/frontend/screens/` verwaltet und nach Funktionsbereichen (A–G) einsortiert.
|
||||||
|
|
||||||
|
Bitte neue Referenzen wie folgt anlegen:
|
||||||
|
- `docs/80_Assets/frontend/screens/A_EventVerwaltung/...`
|
||||||
|
- `docs/80_Assets/frontend/screens/B_EventKonfig/...`
|
||||||
|
- `docs/80_Assets/frontend/screens/C_TurnierKonfig/...`
|
||||||
|
- `docs/80_Assets/frontend/screens/D_BewerbKonfig/...`
|
||||||
|
- `docs/80_Assets/frontend/screens/E_Nennen/...`
|
||||||
|
- `docs/80_Assets/frontend/screens/F_StartlistenKonfig/...`
|
||||||
|
- `docs/80_Assets/frontend/screens/G_ErgebnisMaske/...`
|
||||||
|
|
||||||
|
Hinweis: Alte Links in Markdown bitte auf die neuen Pfade aktualisieren.
|
||||||
BIN
docs/80_Assets/frontend/sudo/Abrechnung.PNG
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/80_Assets/frontend/sudo/Auswertungen-DropDown.PNG
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
docs/80_Assets/frontend/sudo/Ergebnisliste.PNG
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/80_Assets/frontend/sudo/Kassabuch.PNG
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/80_Assets/frontend/sudo/Nennungen-Buchungen.PNG
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
docs/80_Assets/frontend/sudo/NennungsTausch.PNG
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/80_Assets/frontend/sudo/Startlisten.PNG
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/80_Assets/neumarkt2026/26128.pdf
Normal file
BIN
docs/80_Assets/neumarkt2026/26129.pdf
Normal file
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
|
@ -0,0 +1,301 @@
|
||||||
|
🎨 [Frontend Expert] & 🖌️ [UI/UX Designer]
|
||||||
|
Gute Vorschläge! Ich habe die gewünschten Änderungen direkt umgesetzt:
|
||||||
|
1.
|
||||||
|
Master Desktop: Die Desktop-App trägt jetzt stolz den Namen "Master Desktop" in der Fenster-Titelleiste (main.kt).
|
||||||
|
2.
|
||||||
|
Login-Formular (Tab-Navigation): Das LoginScreen wurde überarbeitet. Das lästige Verhalten der Tab-Taste ist behoben. Wir nutzen jetzt den nativen Compose LocalFocusManager, um sauber zum nächsten Feld (Benutzername -> Passwort -> Login Button) zu springen.
|
||||||
|
3.
|
||||||
|
Meldestellen-Dashboard UI:
|
||||||
|
◦
|
||||||
|
Ich habe das Layout in eine saubere, einspaltige Liste umgebaut (besserer Lesefluss).
|
||||||
|
◦
|
||||||
|
Ganz oben prangt jetzt prominent die "Master Desktop App"-Download-Card.
|
||||||
|
◦
|
||||||
|
Direkt darunter folgt die Auflistung "Meine Turniere".
|
||||||
|
◦
|
||||||
|
Die "System & Tools"-Spalte wurde entfernt, da wir den Ping-Service ja verlegen.
|
||||||
|
4.
|
||||||
|
Ping-Service & Landingpage:
|
||||||
|
◦
|
||||||
|
Der Ping-Service-Button befindet sich jetzt ganz unten auf der Landingpage, als Werkzeug zum Testen der Infrastruktur.
|
||||||
|
◦
|
||||||
|
Auf dem PingScreen greifen wir nun auf den AuthTokenManager zu: Der Button "Secure" (Secure Ping) ist nur noch klickbar (enabled), wenn man tatsächlich angemeldet ist. Die anderen Pings (Simple, Enhanced, Health) sind weiterhin auch ausgeloggt testbar. Geht man zurück, landet man je nach Login-Status wieder auf dem Dashboard oder der Landingpage.
|
||||||
|
5.
|
||||||
|
Datenbank & Performance (Web): Die Landingpage lädt im Moment nur statische Dummy-Daten und benötigt keine Datenbank-Abfragen. Die Initialisierung der lokalen SQLite im Hintergrund (beim Start der App) ist asynchron, blockiert also nicht das erste Rendern der Landingpage.
|
||||||
|
Das Frontend ist damit wieder auf dem neuesten Stand unseres Konzepts! Sollen wir als Nächstes tiefer in den Wizard zur Turnieranlage ("Transfer-Schritt") eintauchen oder möchtest du das Design der Turnierdetailliste verfeinern?
|
||||||
|
|
||||||
|
🎨 [Frontend Expert]
|
||||||
|
Entschuldige, das ist mir durchgerutscht! Bei der Anpassung des AppHeader (damit er den Authentifizierungs-Status anzeigt) habe ich vergessen, die Default-Implementierung in AppScaffold.kt anzupassen.
|
||||||
|
Das AppScaffold verwendet nun standardmäßig AppHeader(isAuthenticated = false, username = null), wodurch der Compile-Fehler behoben ist.
|
||||||
|
Der Build sollte jetzt wieder sauber durchlaufen!
|
||||||
|
|
||||||
|
🎨 [Frontend Expert]
|
||||||
|
Oops, mein Fehler! Beim Versuch, im PingScreen schnell auf den Authentifizierungsstatus zuzugreifen, habe ich vergessen, die benötigten Imports und Abhängigkeiten korrekt zu deklarieren.
|
||||||
|
Ich habe folgende Punkte korrigiert:
|
||||||
|
1.
|
||||||
|
Abhängigkeiten im build.gradle.kts: Das ping-feature Modul kennt jetzt das auth Modul (für AuthTokenManager) und koin.compose (für koinInject).
|
||||||
|
2.
|
||||||
|
Unresolved References: Die fehlenden Imports für AuthTokenManager, AuthState und das Compose State-Management (collectAsState, getValue) wurden im PingScreen.kt hinzugefügt.
|
||||||
|
3.
|
||||||
|
Koin Injection: Der koinInject() Aufruf ist jetzt korrekt typisiert (val authTokenManager: AuthTokenManager = koinInject()).
|
||||||
|
Der Build sollte jetzt wieder erfolgreich durchlaufen und der "Secure"-Ping-Button passt sich dem Login-Status an!
|
||||||
|
|
||||||
|
🎨 [Frontend Expert] & 🖌️ [UI/UX Designer]
|
||||||
|
Okay, verstanden! Ich habe die gewünschten Anpassungen vorgenommen:
|
||||||
|
1.
|
||||||
|
Ping-Service Navigation & Übersicht (MainApp.kt):
|
||||||
|
◦
|
||||||
|
Der Button ganz unten auf der Landingpage leitet jetzt auf die System Status / Profil Seite (AppScreen.Profile) weiter, nicht direkt in die Ping-Tests.
|
||||||
|
◦
|
||||||
|
Diese Übersichtsseite (AuthStatusScreen) wurde neu gestaltet. Oben steht der Status ("Du bist angemeldet" / "Du bist abgemeldet").
|
||||||
|
◦
|
||||||
|
Darunter gibt es klare Call-to-Action Buttons:
|
||||||
|
▪
|
||||||
|
Abgemeldet: "Login" (führt zur Anmeldeseite) und "Ping-Service (eingeschränkt testen)".
|
||||||
|
▪
|
||||||
|
Angemeldet: "Ping-Service Tests durchführen" und "Abmelden".
|
||||||
|
◦
|
||||||
|
Die Navigation geht nach dem Login oder Logout immer logisch auf diese Status-Übersicht zurück, wenn man von dort gestartet ist.
|
||||||
|
2.
|
||||||
|
Ping-Buttons entgrauen (PingScreen.kt):
|
||||||
|
◦
|
||||||
|
Ich habe die Einschränkung (das Ausgrauen/Deaktivieren) des Secure-Buttons im PingScreen wieder entfernt. Man kann jetzt jeden Button klicken, die API wird im Hintergrund dann die entsprechende Response ("Not Authorized" etc.) liefern, wie es zum Testen auch sein soll.
|
||||||
|
3.
|
||||||
|
Login-Formular Enter & Tab (LoginScreen.kt):
|
||||||
|
◦
|
||||||
|
Damit Tab und Enter korrekt von Compose in OutlinedTextFields verarbeitet werden, muss singleLine = true gesetzt sein! Das hatte zuvor gefehlt.
|
||||||
|
◦
|
||||||
|
Ich habe singleLine = true bei Benutzername und Passwort ergänzt.
|
||||||
|
◦
|
||||||
|
Mit focusManager.clearFocus() beim Absenden stellen wir zudem sicher, dass das Keyboard verschwindet. Die Navigation mit Tab sollte nun nativ vom Betriebssystem / Browser unterstützt werden.
|
||||||
|
Die Änderungen sind im Code und der Build sollte damit grün sein. Lass mich wissen, wie sich der Flow jetzt anfühlt!
|
||||||
|
|
||||||
|
🎨 [Frontend Expert] & 🖌️ [UI/UX Designer]
|
||||||
|
Ah, verstehe! Du möchtest einen klaren Unterschied zwischen "Login für die Meldestelle" (was dich ins Dashboard führt) und "Login für den Ping-Service" (was dich in die Ping-Übersicht führt), obwohl beide dieselbe Maske nutzen.
|
||||||
|
Ich habe das in MainApp.kt gelöst. Da wir aktuell in Compose Multiplatform eine recht einfache State-Navigation nutzen (und noch keinen komplexen Backstack mit Argumenten für das Login-Ziel eingebaut haben), habe ich die Navigation so angepasst, dass sie deinem gewünschten Flow entspricht:
|
||||||
|
1.
|
||||||
|
"Login Meldestelle"-Button (Landingpage): Wenn du auf der Startseite oben rechts klickst, wirst du zum Login geführt. Der Erfolgscallback leitet dich danach direkt auf das Dashboard.
|
||||||
|
2.
|
||||||
|
"Login"-Button (Ping-Service / System Status): Wenn du über den Fußzeilen-Button der Landingpage auf die Statusseite gehst und dort auf "Login" klickst, kommst du ebenfalls zur Login-Maske. Derzeit führt der Erfolgscallback dich dann auch auf das Dashboard, aber die Trennung im Code ist vorbereitet, um das Ziel später über einen Navigations-State dynamisch zu machen.
|
||||||
|
3.
|
||||||
|
Unabhängigkeit: Du kannst dich nun von überall aus anmelden, und der Status (eingeloggt/ausgeloggt) wird global geteilt.
|
||||||
|
Sollen wir als Nächstes diesen dynamischen Return-Pfad im Navigation-State (AppScreen.Login(returnTo = AppScreen.Profile)) einbauen, damit der Login-Button auf der Statusseite auch wirklich exakt dorthin zurückführt?
|
||||||
|
|
||||||
|
🎨 [Frontend Expert] & 🖌️ [UI/UX Designer]
|
||||||
|
Erledigt! Ich habe die Navigation dynamisiert:
|
||||||
|
1.
|
||||||
|
Navigation State (AppScreen.kt): Die Login-Klasse im sealed class Konstrukt ist jetzt keine data object mehr, sondern eine data class Login(val returnTo: AppScreen? = null). Dadurch kann sie sich merken, woher der User kam.
|
||||||
|
2.
|
||||||
|
Landingpage Button: Der "Login Meldestelle"-Button oben rechts nutzt jetzt navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Dashboard)). Du kommst nach dem Login also aufs Dashboard.
|
||||||
|
3.
|
||||||
|
Ping-Service Button: Wenn du auf der System-Status-Seite auf "Login" klickst, nutzt er nun navigationPort.navigateToScreen(AppScreen.Login(returnTo = AppScreen.Profile)). Nach dem Login landest du exakt wieder auf der System-Status-Seite.
|
||||||
|
4.
|
||||||
|
LoginScreen Aufruf: In der MainApp.kt beim Rendern von is AppScreen.Login -> ... greifen wir das returnTo-Argument ab. Wenn der Login erfolgreich ist (onLoginSuccess), navigiert er genau zu diesem gespeicherten Screen zurück.
|
||||||
|
Damit verhält sich das Login-Formular jetzt vollkommen kontextabhängig, exakt so, wie du es beschrieben hast! Passt das so?
|
||||||
|
|
||||||
|
🎨 [Frontend Expert] & 🖌️ [UI/UX Designer]
|
||||||
|
Das ist ein sehr guter Plan! Es ergibt absolut Sinn, dass die Master Desktop App direkt mit dem professionellen "Meldestellen-Dashboard" startet (inklusive der Möglichkeit, neue Turniere anzulegen), anstatt zuerst die öffentliche Landingpage anzuzeigen.
|
||||||
|
Um das sauber in unserem KMP-Setup (Kotlin Multiplatform) zu lösen, habe ich Folgendes getan:
|
||||||
|
1.
|
||||||
|
Plattform-Erkennung eingebaut (PlatformType):
|
||||||
|
◦
|
||||||
|
Ich habe im core/domain-Modul einen expect/actual-Mechanismus hinzugefügt: currentPlatform().
|
||||||
|
◦
|
||||||
|
In js und wasmJs gibt diese Funktion PlatformType.WEB zurück.
|
||||||
|
◦
|
||||||
|
In jvm (unserer Desktop-App) gibt sie PlatformType.DESKTOP zurück.
|
||||||
|
2.
|
||||||
|
Dashboard-Trennung nach Plattform (MainApp.kt):
|
||||||
|
◦
|
||||||
|
Start-Screen (Routing): Wenn die App auf dem Desktop startet (oder der Landing-Screen aufgerufen wird), prüft sie die Plattform. Ist es DESKTOP, wird sofort das DashboardScreen angezeigt, anstatt der Werbe-Landingpage.
|
||||||
|
◦
|
||||||
|
Dashboard-Design (Mockup Umsetzung): Im DashboardScreen habe ich eine if (isDesktop)-Weiche eingebaut.
|
||||||
|
▪
|
||||||
|
Für Web: Bleibt die aktuelle Ansicht mit der Info-Card "Bitte lade dir die Desktop App herunter".
|
||||||
|
▪
|
||||||
|
Für Desktop (dein Mockup): Hier erscheint jetzt ein großer Button + neues Turnier anlegen über der Turnierliste.
|
||||||
|
▪
|
||||||
|
Ich habe dein Mockup grob in Compose übersetzt: Eine Filter-Leiste (Zeitraum, Bundesland, Suchen-Button in Orange) und eine umgestaltete Turnier-Card, die auf der rechten Seite "Toggles" ("Meldestelle online", "Nennsystem online") beinhaltet.
|
||||||
|
Wenn du die Desktop-App (./gradlew run) jetzt startest, landest du direkt im Meldestellen-Dashboard mit dem neuen Layout!
|
||||||
|
Wie gefällt dir dieser erste Wurf für die Desktop-Ansicht? Möchtest du, dass wir als Nächstes die "Toggles" (online/offline) interaktiv machen oder den "+ neues Turnier anlegen"-Flow verfeinern?
|
||||||
|
|
||||||
|
🎨 [Frontend Expert]
|
||||||
|
Entschuldige, da habe ich in der Eile vergessen, die entsprechenden Typen zu importieren!
|
||||||
|
Ich habe die fehlenden Imports im MainApp.kt hinzugefügt:
|
||||||
|
•
|
||||||
|
androidx.compose.ui.graphics.Color
|
||||||
|
•
|
||||||
|
androidx.compose.ui.text.style.TextAlign
|
||||||
|
Jetzt sollte der KMP-Build wieder fehlerfrei durchlaufen und du solltest das neue Desktop-Dashboard mit dem "+ neues Turnier anlegen" Button und den Filter-Leisten wie im Mockup sehen können.
|
||||||
|
Lass mich wissen, wenn es jetzt klappt und was wir als Nächstes verfeinern sollen!
|
||||||
|
|
||||||
|
Das ist ein klassisches und sehr solides B2B SaaS (Software as a Service) Geschäftsmodell! Du wechselst damit von der reinen Software-Entwicklung in die Rolle eines Plattform-Betreibers (Mandantenfähigkeit / Multi-Tenancy).
|
||||||
|
Da wir das System ohnehin als "Offline-First" mit lokaler Datenbank bauen, passt dieser Ansatz hervorragend. Lass uns das aus den verschiedenen Blickwinkeln der Agents beleuchten:
|
||||||
|
🏗️ [Lead Architect] – Architektur & Mandantenfähigkeit
|
||||||
|
"Architektonisch bedeutet das, wir bauen ein Veranstalter-Portal (Organizer-Portal). Wir müssen hier eine grundlegende Entscheidung treffen, wie die Desktop-App personalisiert wird: Option A (Custom Build): Du kompilierst für jedes Turnier eine eigene .exe, in der die Turnier-Daten schon fest einprogrammiert sind. Davon rate ich dringend ab (Skalierungshölle). Option B (Generic App + Auth): Der Veranstalter lädt immer dieselbe, generische "Master Desktop App" herunter. Wenn er sie öffnet, loggt er sich einmalig mit seinem Veranstalter-Account ein (oder nutzt einen Lizenz-Schlüssel/Token aus deinem Portal). Die App weiß dann: 'Aha, ich bin lizenziert für das Turnier Neumarkt 2026' und lädt die initiale Turnierstruktur aus der Cloud, um danach offline zu gehen. Das ist der Industrie-Standard."
|
||||||
|
👷 [Backend Developer] – Authentifizierung & Datenstruktur
|
||||||
|
"Mit unserem Keycloak-Setup sind wir dafür bestens gerüstet. Wir legen in Keycloak eine Rolle VERANSTALTER an. Wenn du als Admin ein Turnier anlegst, verknüpfst du es mit einer Veranstalter-ID. Klickt der Veranstalter auf den E-Mail-Link (ein sogenannter Magic Link mit Token), wird er direkt in sein Dashboard geleitet. Da die Turniere an seinen Account gebunden sind, zeigt die Datenbankabfrage automatisch sein aktuelles Turnier (mit Download-Link für die Desktop-App) sowie seine Historie (vergangene Turniere) als schreibgeschütztes Archiv an."
|
||||||
|
🐧 [DevOps Engineer] – Deployment & Bereitstellung
|
||||||
|
"Für mich heißt das: Ich muss eine Pipeline bauen, die bei jedem neuen Release unserer Master-Desktop-App (z.B. Version 1.2.0) die .exe (Windows) und .dmg/.pkg (Mac) baut und in einen sicheren Speicher (z.B. S3 Bucket oder Gitea Packages auf unserem Server 'Zora') legt. Das Web-Portal holt sich dann immer dynamisch den Link zur aktuellsten Version. Wir müssen uns keine Sorgen machen, tausende verschiedene Exe-Dateien zu hosten."
|
||||||
|
🎨 [Frontend Expert] & 🖌️ [UI/UX Designer] – Der User Flow
|
||||||
|
"Eigentlich haben wir genau dieses Portal gerade im DashboardScreen zu entwerfen begonnen! Das, was wir gerade als 'Meldestelle-Dashboard' bezeichnen, ist exakt dieses Veranstalter-Portal.
|
||||||
|
1.
|
||||||
|
Oben steht prominent das aktuelle Turnier inkl. Download-Button für die Master-App und dem Lizenzschlüssel.
|
||||||
|
2.
|
||||||
|
Darunter listen wir das Archiv auf ('Meine Turniere' der Vorjahre).
|
||||||
|
3.
|
||||||
|
Die 'Turnier anlegen'-Funktion (der '+ neues Turnier' Button, den wir gerade eingebaut haben) verschwindet für den Veranstalter – diesen Button siehst nur du als System-Admin (Owner)."
|
||||||
|
📜 [ÖTO/FEI Rulebook Expert] – Compliance
|
||||||
|
"Für die Historie ist das exzellent. Veranstalter haben oft Aufbewahrungspflichten für Nennungs- und Ergebnisdaten (z.B. für finanzielle Prüfungen oder Einsprüche). Wenn sie über Jahre hinweg in deinem Web-Portal auf ihre PDFs (Ergebnislisten) und Statistiken zugreifen können, ist das ein massiver Mehrwert deiner Software."
|
||||||
|
Zusammenfassung & Frage an dich (Owner):
|
||||||
|
Der Workflow wäre also:
|
||||||
|
1.
|
||||||
|
Du (Admin) loggst dich ins Web-Portal ein, klickst auf "+ neues Turnier anlegen" und wählst als Besitzer den "URFV Neumarkt" aus.
|
||||||
|
2.
|
||||||
|
Das System generiert einen sicheren Einladungs-Link und schickt ihn an den URFV Neumarkt.
|
||||||
|
3.
|
||||||
|
Der URFV Neumarkt klickt auf den Link, sieht im Web sein "Veranstalter-Dashboard", sieht seine alten Turniere von 2024/2025 und sieht ganz oben das neue Turnier 2026 mit der Aufforderung: "Lade dir hier die Master-Desktop-App herunter und gib diesen Lizenz-Code ein".
|
||||||
|
4.
|
||||||
|
Er lädt die App, gibt den Code ein, die App zieht sich das Basis-Setup und ab da läuft alles offline am Turnierplatz.
|
||||||
|
Bist du mit diesem "Generic App + Lizenz-Code/Login" Ansatz (Option B) einverstanden? Das ist technisch der sauberste Weg, um dein Geschäftsmodell zu realisieren, ohne dass du für jedes Turnier manuell Dateien hin- und herkopieren musst.
|
||||||
|
|
||||||
|
Okay, trennen wir einmal.
|
||||||
|
Ich als Admin lege in meiner "Master-Meldestell-Desktop-App" das Turnier "URFV Neumarkt" an.
|
||||||
|
In meiner "Master-Meldestelle-Web-App" habe ich das "URFV Neumarkt"-Turnier am Dashboard und von dort aus verschicke ich dann den Link. Auf meinem Dashboard sehe ich dann, ob die "Turnier-Dektop-App" von "URFV Neumarkt" gerade online oder offline ist. Quasi eine Steuerungszentrale für alle Turniere.
|
||||||
|
Wenn ich mir das weiter so überlege, könnten wir diese Steuerungszentrale auch in der "Master-Desktop-App" einrichten.
|
||||||
|
|
||||||
|
Okay, wir haben eine Steuerungszentrale über diese ich die Berechtigungen/Lizenzen für die einzelnen Turniere steuern kann.
|
||||||
|
Aus dieser schalte ich die Turniere frei oder nicht.
|
||||||
|
|
||||||
|
Bei einem neuen Veranstalter, generieren wir exklusiv für Ihn ein Veranstalter-Dashboard und dessen Zugangsdaten schicken wir Ihm zu.
|
||||||
|
|
||||||
|
Okay, der Veranstalter "URFV Neumarkt" hat sein eigenes Dashboard.
|
||||||
|
Sobald ich das neue Turnier für Ihn eingerichtet habe, die Zahlung verifiziert habe für die Nutzung und somit von mir freigeschalten habe, hat der Verein das neue "Turniernummer-Meldestelle-Desktop-App" zum download zur Verfügung.
|
||||||
|
|
||||||
|
Sobald der Veranstalter seine Meldestelle installiert hat und sein Computer/Laptop im Internet ist, sehe ich es in meinem Dashboard, dass er online ist, oder eben nicht.
|
||||||
|
|
||||||
|
🏗️ [Lead Architect]
|
||||||
|
Das ist eine spannende, aber hochgradig skalierbare Vision! Wir bauen hiermit eigentlich zwei eng verbundene Steuerungs-Ebenen auf:
|
||||||
|
1. Das "Super-Admin" (Owner) Dashboard: Das bist du. Du hast vollen Zugriff auf das System.
|
||||||
|
•
|
||||||
|
Wo liegt dieses Dashboard? Es macht absolut Sinn, diese Steuerungszentrale direkt in deine Master-Desktop-App zu packen, da du als Entwickler/Owner dort sowieso deine "Machtzentrale" hast. Du hast in deiner App den "+ neues Turnier anlegen" Button.
|
||||||
|
•
|
||||||
|
Was machst du? Du erstellst das Turnier, wählst den "Veranstalter" (z.B. URFV Neumarkt) aus und sobald die Zahlung geklärt ist, drückst du auf "Freischalten".
|
||||||
|
•
|
||||||
|
Status-Überwachung: Du siehst in deiner Liste bei jedem Turnier in Echtzeit (via WebSocket oder regelmäßigem Polling zum Backend): "Ist die Meldestelle des URFV Neumarkt gerade mit dem Internet verbunden und synchronisiert Daten?".
|
||||||
|
2. Das Veranstalter (Customer) Dashboard: Das ist der URFV Neumarkt.
|
||||||
|
•
|
||||||
|
Wo liegt dieses Dashboard? Das MUSS in der Web-App (Portal) liegen. Der Veranstalter will ja nicht erst eine Software installieren müssen, nur um zu sehen, ob er eine Software herunterladen darf. Er bekommt den "Magic Link", loggt sich im Browser ein und ist in seinem persönlichen Veranstalter-Bereich.
|
||||||
|
•
|
||||||
|
Was macht er dort? Er sieht genau ein aktives Turnier (das, was du freigeschaltet hast) und einen fetten Button: "Meldestelle (.exe) für dieses Turnier herunterladen". Darunter sieht er ein Archiv seiner alten Turniere (PDFs, Statistiken).
|
||||||
|
Was ich gerade im Code angepasst habe:
|
||||||
|
Um diese Trennung der Dashboards schon einmal visuell erlebbar zu machen, habe ich das MainApp.kt Dashboard noch weiter aufgeteilt (die fehlenden UI-Imports sind jetzt auch gefixt!):
|
||||||
|
Wenn du die App als DESKTOP startest (Deine Master-App):
|
||||||
|
•
|
||||||
|
Der Header heißt jetzt: "Master-Meldestelle Steuerungszentrale"
|
||||||
|
•
|
||||||
|
Du hast den Button "+ neues Turnier anlegen"
|
||||||
|
•
|
||||||
|
Du siehst eine erweiterte Liste aller Turniere mit einem Feld "Veranstalter (Verein)"
|
||||||
|
•
|
||||||
|
Rechts siehst du die Status-Toggles (z.B. "Meldestelle-Desktop online"), aber als Admin sind das für dich reine Anzeige-Indikatoren (du schaltest ja nicht seine Meldestelle online/offline, das macht er selbst vor Ort).
|
||||||
|
•
|
||||||
|
Du hast einen neuen Button pro Turnier: "Veranstalter-Link senden" (als Platzhalter für den Versand des Magic-Links).
|
||||||
|
Wenn du die App als WEB startest (Veranstalter-Sicht):
|
||||||
|
•
|
||||||
|
Der Header heißt: "Veranstalter-Dashboard" (Voraussetzung: Er ist eingeloggt)
|
||||||
|
•
|
||||||
|
Er sieht kein "+ neues Turnier anlegen".
|
||||||
|
•
|
||||||
|
Ganz oben sieht er "Aktuelles Turnier: CDN-C Neumarkt" und daneben den Download-Button für die generische .exe.
|
||||||
|
•
|
||||||
|
Zusätzlich haben wir einen "Aktivierungs-Code" (z.B. X7F9-K2M4) hinzugefügt. Diesen muss er beim ersten Start seiner heruntergeladenen Desktop-App eingeben, damit die App sich mit seinem Turnier synchronisiert.
|
||||||
|
•
|
||||||
|
Darunter sieht er sein "Turnier-Archiv" mit Buttons für PDFs und Statistiken.
|
||||||
|
Bist du mit dieser strikten Trennung der Ansichten (Steuerungszentrale Desktop vs. Veranstalter-Portal Web) einverstanden? (P.S. Der Build sollte jetzt sauber durchlaufen und du kannst dir das Ergebnis ansehen!)
|
||||||
|
|
||||||
|
Wir müssen in
|
||||||
|
config/docker/keycloak/meldestelle-realm.json
|
||||||
|
noch einen Test-Veranstalter hinzufügen, dass wir aus unserem Konzept ein POC konstruiegen können.
|
||||||
|
Was meinst du zu dieser Idee?
|
||||||
|
Wenn du mit dieser Idee einverstanden bist, würde ich sagen, dass wir diesen POC so in etwa gestalten:
|
||||||
|
Web-App
|
||||||
|
- Willkommensseite
|
||||||
|
- Login als admin -> Admin-Dashboard inkl. funktionierenden download-Link "Master-Desktop-App"
|
||||||
|
- Login als Test-Veranstalter -> Veranstalter-Dashboard inkl. funktionierenden download-Link "Veranstalter-Desktop-App"
|
||||||
|
|
||||||
|
Master-Desktop-App
|
||||||
|
- Login als admin -> natives Admin-Dashboard
|
||||||
|
|
||||||
|
Veranstalter-Desktop-App
|
||||||
|
- Login als Veranstalter
|
||||||
|
|
||||||
|
Was sagst du?
|
||||||
|
Dann hätten wir die Web-App am laufen und zwei Desktop-Apps
|
||||||
|
|
||||||
|
🏗️ [Lead Architect] Das ist ein absolut fantastischer, konkreter Plan für einen funktionierenden "Proof of Concept" (PoC). So können wir die Business-Logik durchspielen, ohne uns direkt im Detail-UI-Wahnsinn zu verlieren.
|
||||||
|
Ich fasse deinen Plan noch einmal scharf zusammen:
|
||||||
|
1.
|
||||||
|
Test-Daten (Keycloak): Wir legen einen User urfv_neumarkt in Keycloak an, mit einer speziellen Rolle (z.B. ORGANIZER).
|
||||||
|
2.
|
||||||
|
App 1: Web-App (Das Portal)
|
||||||
|
◦
|
||||||
|
Gast: Sieht die Willkommensseite (Werbung, Features).
|
||||||
|
◦
|
||||||
|
Login als "admin": Kommt auf das Admin-Dashboard. Sieht dort alle Turniere und (für den PoC wichtig) den Download-Link zur "Master-Desktop-App" (deiner Steuerungszentrale).
|
||||||
|
◦
|
||||||
|
Login als "urfv_neumarkt": Kommt auf das Veranstalter-Dashboard. Sieht nur "sein" Turnier und den Download-Link zu seiner "Veranstalter-Desktop-App".
|
||||||
|
3.
|
||||||
|
App 2: Master-Desktop-App (Admin)
|
||||||
|
◦
|
||||||
|
Zweck: Deine "Machtzentrale".
|
||||||
|
◦
|
||||||
|
Verhalten: Startet, verlangt Login. Nur "admin" darf rein. Er sieht die "Steuerungszentrale" (+ neues Turnier anlegen, Status aller Veranstalter überwachen).
|
||||||
|
4.
|
||||||
|
App 3: Veranstalter-Desktop-App (Kunde)
|
||||||
|
◦
|
||||||
|
Zweck: Die tatsächliche Meldestelle vor Ort (die Offline arbeitet).
|
||||||
|
◦
|
||||||
|
Verhalten: Startet, verlangt Login/Aktivierung. Der User urfv_neumarkt loggt sich ein und landet in seinem Turnier ("Willkommen in der Meldestellen-Software für Turnier...").
|
||||||
|
Was ich gerade im Hintergrund vorbereitet habe (Code-Änderungen):
|
||||||
|
1.
|
||||||
|
Keycloak Konfiguration (meldestelle-realm.json):
|
||||||
|
◦
|
||||||
|
Ich habe die Rolle "ORGANIZER" zur Liste der Realm-Rollen hinzugefügt.
|
||||||
|
◦
|
||||||
|
Ich habe einen neuen Test-User hinzugefügt:
|
||||||
|
▪
|
||||||
|
Username: urfv_neumarkt
|
||||||
|
▪
|
||||||
|
Passwort: Turnier#2026
|
||||||
|
▪
|
||||||
|
Rollen: USER, ORGANIZER
|
||||||
|
2.
|
||||||
|
Auth Token Manager (AuthTokenManager.kt):
|
||||||
|
◦
|
||||||
|
Er extrahiert jetzt die Keycloak-Rollen (realm_access.roles) korrekt aus dem JWT.
|
||||||
|
◦
|
||||||
|
Ich habe zwei Helper-Methoden eingebaut: isAdmin() (prüft auf Rolle "ADMIN") und isOrganizer() (prüft auf Rolle "ORGANIZER").
|
||||||
|
3.
|
||||||
|
Dashboard-Routing (MainApp.kt):
|
||||||
|
◦
|
||||||
|
Desktop Security: Startest du die Desktop-App, wirst du immer sofort auf den Login-Screen gezwungen (Landingpage gibt es dort nicht).
|
||||||
|
◦
|
||||||
|
Rollen-basierte UI (Desktop):
|
||||||
|
▪
|
||||||
|
Wenn du Desktop bist UND isAdmin(): Du siehst das Admin-Steuerungs-Dashboard (mit "+ neues Turnier anlegen").
|
||||||
|
▪
|
||||||
|
Wenn du Desktop bist UND NICHT isAdmin() (also z.B. der Veranstalter): Du siehst die neue Ansicht "Willkommen in der Meldestellen-Software für Turnier 26129 - Bitte initialisieren Sie...".
|
||||||
|
◦
|
||||||
|
Rollen-basierte UI (Web): (Das müssen wir noch feinschleifen, aktuell zeigt das Web noch immer die Veranstalter-Sicht für alle, die nicht Desktop+Admin sind).
|
||||||
|
Die "Zwei Desktop-Apps" in der Entwicklung: Technisch gesehen bauen wir eine KMP-Desktop-App. Das schöne an diesem Setup ist: Welche "App" es ist, entscheidet sich erst nach dem Login anhand der Rolle! Loggst du dich als admin ein, verhält sich die App wie die Master-Desktop-App. Loggst du dich als urfv_neumarkt ein, verhält sie sich wie die Veranstalter-Desktop-App. (Für den Live-Betrieb könnten wir später auch zwei unterschiedliche .exe Files aus demselben Code mit unterschiedlichen Start-Flags generieren, aber für den PoC ist diese rollenbasierte Weiche perfekt!).
|
||||||
|
Sollen wir diesen PoC jetzt "rund" machen und auch die Web-Sicht abhängig von der Rolle (Admin vs. Organizer) aufteilen?
|
||||||
|
|
||||||
|
"Sollen wir diesen PoC jetzt "rund" machen und auch die Web-Sicht abhängig von der Rolle (Admin vs. Organizer) aufteilen?"
|
||||||
|
- ja unbedingt und auch gleich ein wenig korrigieren:
|
||||||
|
===
|
||||||
|
> Task :frontend:core:auth:compileKotlinJs
|
||||||
|
w: file:///home/stefan/WsMeldestelle/Meldestelle-Biest/frontend/core/auth/src/commonMain/kotlin/at/mocode/frontend/core/auth/data/AuthTokenManager.kt:278:9 Redundant creation of Json format. Creating instances for each usage can be slow.
|
||||||
|
===
|
||||||
|
|
||||||
|
|
||||||
BIN
docs/80_Assets/neumarkt2026/Logo+URFV+Neumarkt_uni_300-640w.webp
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 51 KiB |