Refactor Veranstalter and Veranstaltung flows: add VeranstalterProfil UI, event creation callback, profile enhancements, and save-enable matrix logic. Extend ZNS import and branding workflows.
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Has been cancelled

This commit is contained in:
2026-04-01 02:49:22 +02:00
parent f44b2c8126
commit 09debdef86
14 changed files with 1163 additions and 43 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,409 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ergebnisliste</title>
<style>
body {
font-family: 'Tahoma', 'Verdana', sans-serif;
background-color: #F0F0F0;
margin: 0;
padding: 5px;
box-sizing: border-box;
color: black;
font-size: 11px;
}
#window {
border: 1px solid #999;
box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
overflow: hidden;
background-color: #F0F0F0;
}
#title-bar {
background: linear-gradient(to right, #005A9C 0%, #0099CC 100%);
color: white;
padding: 3px 6px;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #CCC;
}
#title-controls {
display: flex;
gap: 2px;
}
.title-btn {
width: 16px;
height: 16px;
background-color: white;
color: black;
border: 1px solid #333;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-weight: normal;
}
#main-content {
padding: 5px;
display: grid;
grid-template-columns: 3fr 1fr;
gap: 5px;
}
#left-column {
display: flex;
flex-direction: column;
gap: 5px;
}
.grid-header {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 1px;
background-color: #EAEAEA;
border: 1px solid #CCC;
}
.grid-item {
border: 1px solid #CCC;
padding: 2px;
text-align: center;
font-size: 10px;
}
.pane {
background-color: white;
border: 1px solid #CCC;
min-height: 200px;
overflow: auto;
}
.toolbar {
display: flex;
gap: 8px;
align-items: center;
background-color: #EAEAEA;
padding: 3px;
border: 1px solid #CCC;
border-bottom: none;
}
.tool-link {
color: black;
text-decoration: none;
cursor: pointer;
}
.tool-link:hover {
text-decoration: underline;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 10px;
}
th {
background-color: #EAEAEA;
border: 1px solid #CCC;
padding: 2px 4px;
text-align: left;
font-weight: normal;
}
td {
border-bottom: 1px solid #EEE;
padding: 2px 4px;
}
#middle-form {
background-color: #F0F0F0;
border: 1px solid #CCC;
padding: 5px;
display: flex;
align-items: flex-start;
gap: 10px;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 2px;
}
.radio-group label {
display: flex;
align-items: center;
gap: 3px;
}
.form-inputs {
display: flex;
flex-direction: column;
gap: 3px;
align-items: flex-start;
}
.input-pair {
display: flex;
gap: 3px;
}
.small-input {
width: 25px;
border: 1px solid #AAA;
text-align: center;
}
.form-label {
font-size: 9px;
color: #555;
pointer-events: none;
}
.form-select {
width: 80px;
border: 1px solid #AAA;
}
.button-group {
display: flex;
gap: 3px;
}
.windows-btn {
background-color: #EAEAEA;
border: 1px solid #AAA;
padding: 2px 8px;
cursor: pointer;
box-shadow: 1px 1px 1px rgba(0,0,0,0.1);
}
.windows-btn:hover {
background-color: #F5F5F5;
}
.windows-btn:disabled {
opacity: 0.5;
cursor: default;
}
#right-panels {
display: flex;
flex-direction: column;
gap: 5px;
}
.right-panel {
background-color: #F0F0F0;
border: 1px solid #CCC;
padding: 5px;
}
.panel-title {
margin-bottom: 5px;
font-weight: bold;
}
.control-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 3px;
}
.right-label {
width: 100px;
}
.number-controls {
display: flex;
gap: 1px;
align-items: center;
}
.number-btn {
width: 14px;
height: 14px;
background-color: white;
border: 1px solid #AAA;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 12px;
padding: 0;
}
.checkbox-container {
display: flex;
justify-content: flex-end;
width: calc(100% - 100px);
}
.right-panel-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3px;
margin-top: 5px;
}
</style>
</head>
<body>
<div id="window">
<div id="title-bar">
<span>Ergebnisliste</span>
<div id="title-controls">
<div class="title-btn">_</div>
<div class="title-btn"></div>
<div class="title-btn">x</div>
</div>
</div>
<div id="main-content">
<div id="left-column">
<div class="grid-header">
<div class="grid-item">1</div><div class="grid-item">2</div><div class="grid-item">3</div><div class="grid-item">4</div><div class="grid-item">5</div><div class="grid-item">5</div>
<div class="grid-item">6</div><div class="grid-item">7</div><div class="grid-item">8</div><div class="grid-item">9</div><div class="grid-item">10</div><div class="grid-item"></div>
<div class="grid-item">11</div><div class="grid-item">12</div><div class="grid-item"></div><div class="grid-item"></div><div class="grid-item"></div><div class="grid-item"></div>
<div class="grid-item"></div><div class="grid-item"></div><div class="grid-item"></div><div class="grid-item"></div><div class="grid-item"></div><div class="grid-item"></div>
</div>
<div>
<div class="toolbar">
<span class="tool-link">Aktualisieren</span>
<span class="tool-link">Platzierte</span>
<span class="tool-link">Suchen &#x25BC;</span>
<span style="color: #666;">0 gefunden</span>
<span class="tool-link">Bearbeiten</span>
</div>
<div class="pane">
<table>
<thead>
<tr>
<th>Ergebnis</th><th>Nr.</th><th>KopfNr</th><th>Pferd</th><th>Reiter</th><th>K</th><th>Q</th><th>Platziert</th><th>Wertung</th><th>PGp</th><th>ZGp</th><th>Gesamtnote</th><th>Geldpreis</th><th>Pl. E</th><th>Pl. H</th><th>Pl. C</th><th>Pl. M</th>
</tr>
</thead>
</table>
</div>
</div>
<div id="middle-form">
<div class="radio-group">
<label><input type="radio" name="parcours" checked> Grundparcours</label>
<label><input type="radio" name="parcours"> Stechen 1</label>
<label><input type="radio" name="parcours"> Stechen 2</label>
<label><input type="radio" name="parcours"> Stechen 3</label>
</div>
<div class="form-inputs">
<div class="input-pair">
<div>
<input type="text" class="small-input">
<div class="form-label">Fehler</div>
</div>
<div>
<input type="text" class="small-input">
<div class="form-label">Zeit</div>
</div>
</div>
</div>
<div>
<select class="form-select"></select>
</div>
<div class="button-group">
<button class="windows-btn" disabled>Speichern</button>
<button class="windows-btn">Nächster</button>
<button class="windows-btn" disabled>Abbrechen</button>
</div>
</div>
<div>
<div class="toolbar">
<span class="tool-link">Aktualisieren</span>
<span class="tool-link">Starter</span>
<span class="tool-link">Suchen &#x25BC;</span>
<span class="tool-link">Bearbeiten</span>
</div>
<div class="pane">
<table>
<thead>
<tr>
<th>Pos.</th><th>Nr.</th><th>KopfNr</th><th>Pferd</th><th>Reiter</th><th>K</th><th>Q</th><th>Bemerkung</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div id="right-panels">
<div class="right-panel">
<div class="panel-title">Platzierung & Geldpreis:</div>
<div class="control-row">
<span class="right-label">Anzahl Platzierte:</span>
<div class="number-controls">
<button class="number-btn">-</button>
<input type="text" class="small-input">
<button class="number-btn">+</button>
</div>
</div>
<div class="control-row">
<span class="right-label">Geldpreis:</span>
<span>---</span>
</div>
<div class="control-row">
<span class="right-label">Kaderreiter Extra:</span>
<div class="checkbox-container">
<input type="checkbox">
</div>
</div>
<div class="control-row">
<span class="right-label">Anzahl platzierte Kaderreiter:</span>
<input type="text" class="small-input">
</div>
<div class="control-row">
<span class="right-label">Geldpreis für Kaderreiter:</span>
<span>---</span>
</div>
<div class="control-row">
<span class="right-label">Summe Geldpreise:</span>
<span>---</span>
</div>
</div>
<div class="right-panel">
<div class="panel-title">Bewerb:</div>
<div class="right-panel-buttons">
<button class="windows-btn">Abschließen</button>
<button class="windows-btn">Öffnen</button>
</div>
</div>
<div class="right-panel">
<div class="panel-title">Ergebnisliste:</div>
<div class="right-panel-buttons">
<button class="windows-btn">Import</button>
<button class="windows-btn">Pfad</button>
<button class="windows-btn">Export</button>
<button class="windows-btn">Drucken</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

@@ -0,0 +1,323 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nennmaske Entwurf</title>
<style>
:root {
--bg-color: #f4f7f6;
--border-color: #d1d5db;
--primary-blue: #3f51b5;
--text-main: #333;
--header-bg: #ffffff;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
margin: 0;
padding: 10px;
font-size: 13px;
color: var(--text-main);
}
/* Layout Struktur */
.container {
display: grid;
grid-template-rows: 1fr auto 1fr;
gap: 10px;
height: 95vh;
}
.top-row {
display: grid;
grid-template-columns: 1fr 1fr 1.2fr;
gap: 10px;
}
.bottom-row {
display: grid;
grid-template-columns: 1.5fr 1fr;
gap: 10px;
}
/* Gemeinsame Panel-Styles */
.panel {
background: white;
border: 1px solid var(--border-color);
display: flex;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
}
.panel-header {
padding: 8px;
background: #fff;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
gap: 5px;
}
.panel-content {
flex-grow: 1;
overflow-y: auto;
background: #fff;
position: relative;
}
.empty-state {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
color: #888;
}
/* Formular Elemente */
input[type="text"] {
flex-grow: 1;
padding: 5px;
border: 1px solid var(--border-color);
border-radius: 3px;
}
button {
padding: 5px 12px;
border: 1px solid var(--border-color);
background: white;
cursor: pointer;
border-radius: 3px;
}
.btn-primary {
border-color: var(--primary-blue);
color: var(--primary-blue);
}
.action-bar-center {
display: flex;
justify-content: center;
gap: 10px;
padding: 10px 0;
}
.nav-btn {
background: var(--primary-blue);
color: white;
border: none;
padding: 8px 20px;
border-radius: 6px;
display: flex;
align-items: center;
gap: 5px;
}
/* Tabellen Styles */
table {
width: 100%;
border-collapse: collapse;
}
th {
background: #f9fafb;
text-align: left;
padding: 8px;
border-bottom: 1px solid var(--border-color);
font-weight: 600;
}
td {
padding: 6px 8px;
border-bottom: 1px solid #f0f0f0;
}
.row-selected {
background-color: #fff9c4;
}
/* Spezifische Anpassungen */
.footer-btns {
padding: 8px;
display: flex;
gap: 10px;
border-top: 1px solid var(--border-color);
}
.footer-btns button {
flex: 1;
}
.tabs {
display: flex;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 8px 15px;
cursor: pointer;
border-bottom: 2px solid transparent;
}
.tab.active {
color: var(--primary-blue);
border-bottom: 2px solid var(--primary-blue);
font-weight: bold;
}
.qty-input {
width: 40px;
text-align: center;
border: 1px solid var(--border-color);
}
</style>
</head>
<body>
<div class="container">
<div class="top-row">
<div class="panel">
<div class="panel-header">
<strong>Pferd:</strong>
<input type="text" placeholder="Kopfnummer oder Name">
<button>...</button>
<button>Leeren</button>
</div>
<div class="panel-content">
<div class="empty-state">Keine Ergebnisse</div>
</div>
<div class="footer-btns">
<button class="btn-primary">Neu</button>
<button disabled>Bearbeiten</button>
</div>
</div>
<div class="panel">
<div class="panel-header">
<strong>Reiter:</strong>
<input type="text" placeholder="Vorname und/oder Nachname">
<button>...</button>
<button>Leeren</button>
</div>
<div class="panel-content">
<div class="empty-state">Keine Ergebnisse</div>
</div>
<div class="footer-btns">
<button class="btn-primary">Neu</button>
<button disabled>Bearbeiten</button>
</div>
</div>
<div class="panel">
<div class="tabs">
<div class="tab active">VERKAUF</div>
<div class="tab">BUCHUNGEN</div>
</div>
<div class="panel-header" style="justify-content: space-between;">
<span>11 Artikel</span>
<div>
<a href="#" style="font-size: 11px; margin-right: 10px;">Rückgängig</a>
<a href="#" style="font-size: 11px; font-weight: bold;">Speichern</a>
</div>
</div>
<div class="panel-content">
<table>
<thead>
<tr>
<th>KNr</th>
<th>+/-</th>
<th>Menge</th>
<th>Buchungstext</th>
<th>Betrag</th>
</tr>
</thead>
<tbody>
<tr><td></td><td>+</td><td><input class="qty-input" value="0"></td><td>Belastung</td><td>0.00</td></tr>
<tr class="row-selected"><td></td><td>+</td><td><input class="qty-input" value="0"></td><td>Gutschrift</td><td>0.00</td></tr>
<tr><td></td><td>+</td><td><input class="qty-input" value="0"></td><td>Boxenpauschale</td><td>0.00</td></tr>
<tr><td></td><td>+</td><td><input class="qty-input" value="0"></td><td>Ansage</td><td>0.00</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="action-bar-center">
<button class="nav-btn">☰ Startliste</button>
<button class="nav-btn">🏆 Ergebnisse</button>
<button class="nav-btn">📄 Abrechnung</button>
</div>
<div class="bottom-row">
<div class="panel">
<div class="tabs">
<div class="tab active">REITER</div>
<div class="tab">PFERD</div>
<div class="tab">BEWERBE</div>
</div>
<div class="panel-header" style="justify-content: space-between; background: #fafafa;">
<span>0 Nennungen</span>
<div style="color: var(--primary-blue);">
<span style="margin-right: 10px; cursor: pointer;">Positionieren</span>
<span style="cursor: pointer;">Stornieren</span>
</div>
</div>
<div class="panel-content">
<table>
<thead>
<tr>
<th>Tag</th>
<th>Pl.</th>
<th>Bewerb</th>
<th>Bewerbsname</th>
<th>Bemerkung</th>
<th>Pferd</th>
</tr>
</thead>
</table>
<div class="empty-state" style="margin-top: 50px;">Keine Nennungen vorhanden</div>
</div>
</div>
<div class="panel">
<div class="panel-header" style="justify-content: space-between;">
<strong>Bewerbsübersicht</strong>
</div>
<div class="panel-header" style="justify-content: space-between; font-size: 11px;">
<span>12 Bewerbe</span>
<span style="color: var(--primary-blue);">Filtern</span>
</div>
<div class="panel-content">
<table>
<thead>
<tr>
<th>Tag</th>
<th>Pl.</th>
<th>Bew.</th>
<th>Beginn</th>
<th>Nenn.</th>
<th>Bewerbsname</th>
</tr>
</thead>
<tbody>
<tr><td>So</td><td>1</td><td><strong>1</strong></td><td>08:00</td><td>0</td><td>Dressurreiterprüfung Ratepass</td></tr>
<tr><td>So</td><td>1</td><td><strong>2</strong></td><td>08:20</td><td>0</td><td>Dressurreiterprüfung Katecnadel</td></tr>
<tr><td>So</td><td>1</td><td><strong>3</strong></td><td>08:40</td><td>0</td><td>Dressurreiterprüfung Idf.</td></tr>
<tr><td>So</td><td>1</td><td><strong>4</strong></td><td>09:00</td><td>0</td><td>Dressurprüfung Idf.</td></tr>
</tbody>
</table>
</div>
<div style="padding: 5px; text-align: center; font-size: 11px; color: #777;">
Bitte wählen Sie zuerst ein Pferd und einen Reiter aus
</div>
</div>
</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

@@ -0,0 +1,47 @@
# 🧹 Curator Session Log (2026-04-01)
## Zusammenfassung
- Flow-Entscheidung bestätigt: Grüner Pfad aktiv, roter Pfad verworfen. "+ Neues Turnier" führt direkt zum Tab „STAMMDATEN“ v2 mit TurnierNr.-Gatekeeping.
- Keine Codeänderungen in dieser Sitzung; Build zuletzt grün. Entscheidungen und nächste Schritte dokumentiert.
## Beschlossene UI/Flow-Regeln
- Turnieranlage
- Einstieg: "+ Neues Turnier" → direkt „Turnier Detail v2“ Tab „STAMMDATEN“.
- Gatekeeping: 5stellige TurnierNr. eingeben + Bestätigungsdialog (danach immutable).
- Save-Enable-Matrix: aktiv nur wenn (Nr bestätigt ∧ ZNS geladen ∧ Datum gültig).
- ZNS-Status
- Panel immer sichtbar, zeigt Quelle, `payloadVersion`, Zeitstempel.
- „ImportLog“ Dialog mit den letzten 5 Einträgen (Erfolg/Fehler, Kurzmeldung).
- Kategorien & Pony
- Mehrfach-Kategorien wie vormittags vereinbart; Pony über KategorienSuffix „P“ (kein separater Switch).
- Kategorien-UI wird gruppiert (z. B. Dressur/Springen).
- Datum/Ort
- Datum im zulässigen Veranstaltungszeitraum; Hinweis: „Muss zwischen [vonbis] liegen“.
- Abweichender TurnierOrt: SoftWarnung (kein HardBlock).
- Branding
- Feld „Titel“ optional. DefaultVorschlag: „[Kategorien] [VereinOrt] [Bundesland]“ (Fallback über Veranstalterdaten).
- „TurnierLogo“ optional; Fallback = VeranstalterLogo.
## Veranstalter-Flow
- Nach „Schritt 2: Vereinsdaten bestätigen“ → Weiterleitung zum „VeranstalterProfil“.
- VeranstalterProfil: minimale Felder (LogoURL, Ansprechpartner, EMail, Telefon, Adresse), CTA „+ Neue Veranstaltung“.
- Von dort → VeranstaltungWizard Schritt 2 („Basisdaten“). Feld „VeranstaltungsLogo“ optional; Fallbacks: VeranstaltungsLogo → VeranstalterLogo → Default.
## Footer-Onboarding
- Online/OfflineStatus anzeigen.
- GeräteVerbindung (z. B. „RichterTurm“) anzeigen, klickbar für Details.
- ChatTrigger anzeigen, wenn mindestens ein weiteres Gerät verbunden ist.
## Nächste Schritte (ToDo)
- Routing final auf Stammdaten v2 festziehen; alte Pfade entfernen.
- SaveEnableMatrix implementieren; ZNSPanel inkl. ImportLog.
- KategorienUI konsolidieren und gruppieren; DefaultTitel generieren; OrtSoftwarnung.
- VeranstalterProfil & ‑Übersicht finalisieren; CTAFlow prüfen.
- FooterOnboarding integrieren (Status, Geräte, ChatTrigger).
## Artefakte/Referenzen
- docs/06_Frontend/flow-wechsel.png (neuer Flow grüner Pfeil)
- docs/06_Frontend/flow-fehler.png (Bruchstellen im alten Flow)
- docs/99_Journal/2026-03-31_Session_Log_Event_First_Workflow.md
- docs/99_Journal/2026-03-30_Session_Log_ZNS_Documentation.md
- docs/99_Journal/2026-03-30_Session_Log_Masterdata_OETO_Consolidation.md