- Added `PferdReiterEingabe` for horse and rider selection with search functionality and keyboard navigation. - Implemented `NennungenTabelle` to display filtered registrations based on selected horse or rider. - Introduced `NennungsMaske` to combine search, table, and competition views for streamlined user interaction. - Extended types with `Veranstalter` interface and mock data for better context and integration. - Documented ÖTO-compliant tournament structure for frontend reference. Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
1051 lines
25 KiB
Markdown
1051 lines
25 KiB
Markdown
# Turnierverwaltungs-Anwendung - Frontend Prototyp
|
|
|
|
## Projektübersicht
|
|
|
|
Dies ist ein professioneller Prototyp einer Turnierverwaltungs-Anwendung für den österreichischen Pferdesportverband (
|
|
ÖPS). Die Anwendung ist als **Desktop-First-Anwendung** konzipiert und bietet eine kompakte, tastaturoptimierte
|
|
Benutzeroberfläche zur Verwaltung von Veranstaltungen, Turnieren und Bewerben im Pferdesport.
|
|
|
|
### Hauptmerkmale
|
|
|
|
- **Desktop-optimierte UI**: Fokus auf kompakte Layouts und effiziente Datenerfassung
|
|
- **Hierarchische Datenstruktur**: Veranstalter (Verein) → Veranstaltungen → Turniere → Bewerbe
|
|
- **Veranstalter-Verwaltung**: Admin legt Veranstalter an → Veranstalter erhält Login → Veranstalter verwaltet eigene
|
|
Veranstaltungen
|
|
- **Material Design 3**: Moderne UI mit Primärfarbe Indigo (#3F51B5)
|
|
- **Tastaturoptimiert**: Effiziente Navigation und Dateneingabe
|
|
- **OETO-Ausschreibungs-Standard**: Tab-Struktur folgt österreichischen Richtlinien
|
|
|
|
---
|
|
|
|
## Technologie-Stack
|
|
|
|
### Core Technologies
|
|
|
|
- **React 18** - UI Framework
|
|
- **TypeScript** - Type-safe JavaScript
|
|
- **React Router** (Data Mode) - Client-side Routing
|
|
- **Material-UI (MUI) v6** - Component Library
|
|
- **Vite** - Build Tool & Development Server
|
|
|
|
### Styling
|
|
|
|
- **Material-UI System** - Sx Props für Styling
|
|
- **Tailwind CSS v4** - Utility Classes (sekundär)
|
|
- **Material Design 3** - Design Language
|
|
|
|
### Package Manager
|
|
|
|
- **pnpm** - Fast, disk space efficient package manager
|
|
|
|
---
|
|
|
|
## Projektstruktur
|
|
|
|
```
|
|
/
|
|
├── src/
|
|
│ ├── app/
|
|
│ │ ├── components/
|
|
│ │ │ ├── veranstaltung/
|
|
│ │ │ │ ├── StammdatenTab.tsx # A-Satz / Stammdaten
|
|
│ │ │ │ ├── OrganisationTab.tsx # Funktionäre & Plätze
|
|
│ │ │ │ ├── PreislisteTab.tsx # Preisliste
|
|
│ │ │ │ └── UebersichtTab.tsx # Transfer/Übersicht
|
|
│ │ │ ├── turnier/
|
|
│ │ │ │ └── BewerbeTab.tsx # Bewerbe-Verwaltung (Hauptseite)
|
|
│ │ │ ├── AdminDrawer.tsx # Haupt-Navigation
|
|
│ │ │ ├── VeranstaltungAnsicht.tsx # Veranstaltungs-View
|
|
│ │ │ └── TurnierAnsicht.tsx # Turnier-View
|
|
│ │ ├── routes.tsx # React Router Konfiguration
|
|
│ │ └── App.tsx # Root Component
|
|
│ ├── styles/
|
|
│ │ ├── theme.css # CSS Variables & Theme
|
|
│ │ └── fonts.css # Font Imports
|
|
│ └── main.tsx # Entry Point
|
|
├── package.json
|
|
└── README.md
|
|
```
|
|
|
|
---
|
|
|
|
## Installation & Setup
|
|
|
|
### Voraussetzungen
|
|
|
|
- **Node.js** >= 18.x
|
|
- **pnpm** >= 8.x (empfohlen) oder npm
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
# Repository klonen
|
|
git clone <repository-url>
|
|
cd turnierverwaltung
|
|
|
|
# Dependencies installieren
|
|
pnpm install
|
|
|
|
# Development Server starten
|
|
pnpm dev
|
|
|
|
# Build für Production
|
|
pnpm build
|
|
|
|
# Preview Production Build
|
|
pnpm preview
|
|
```
|
|
|
|
### Verfügbare Scripts
|
|
|
|
```json
|
|
{
|
|
"dev": "vite", // Development Server auf http://localhost:5173
|
|
"build": "vite build", // Production Build
|
|
"preview": "vite preview" // Preview Production Build
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Architektur & Konzepte
|
|
|
|
### 1. Routing-System (React Router Data Mode)
|
|
|
|
Die Anwendung verwendet React Router's Data Mode Pattern mit einer klar definierten Route-Hierarchie:
|
|
|
|
```typescript
|
|
// src/app/routes.tsx
|
|
const router = createBrowserRouter([
|
|
{
|
|
path: "/",
|
|
Component: Root,
|
|
children: [
|
|
// Neue Veranstaltung
|
|
{
|
|
path: "veranstaltung/neu",
|
|
Component: VeranstaltungAnsicht
|
|
},
|
|
|
|
// Bestehende Veranstaltung
|
|
{
|
|
path: "veranstaltung/:id",
|
|
Component: VeranstaltungAnsicht
|
|
},
|
|
|
|
// Neues Turnier in Veranstaltung
|
|
{
|
|
path: "veranstaltung/:veranstaltungId/turnier/neu",
|
|
Component: TurnierAnsicht
|
|
},
|
|
|
|
// Bestehendes Turnier
|
|
{
|
|
path: "veranstaltung/:veranstaltungId/turnier/:nr",
|
|
Component: TurnierAnsicht
|
|
},
|
|
|
|
// 404 Fallback
|
|
{
|
|
path: "*",
|
|
Component: NotFound
|
|
}
|
|
]
|
|
}
|
|
]);
|
|
```
|
|
|
|
**Wichtig**: Verwenden Sie immer das `react-router` Package (nicht `react-router-dom`), da die Anwendung in einer
|
|
speziellen Umgebung läuft.
|
|
|
|
---
|
|
|
|
### 2. Navigation & Benutzerfluss
|
|
|
|
#### Hauptnavigation: AdminDrawer
|
|
|
|
Die Anwendung verwendet eine **Drawer-Navigation** (links) mit folgenden Bereichen:
|
|
|
|
```
|
|
Admin - Verwaltung
|
|
├── Veranstaltungen
|
|
│ ├── Neue Veranstaltung → /veranstaltung/neu
|
|
│ └── [Liste Veranstaltungen] → /veranstaltung/:id
|
|
│ └── Turniere
|
|
│ ├── Neues Turnier → /veranstaltung/:id/turnier/neu
|
|
│ └── [Turnier-Liste] → /veranstaltung/:id/turnier/:nr
|
|
└── ...
|
|
```
|
|
|
|
#### Login-System
|
|
|
|
- **Demo Credentials**:
|
|
- Username: `admin`
|
|
- Passwort: `Admin#1234`
|
|
- Login-State wird im `localStorage` gespeichert
|
|
- Keine Backend-Integration im Prototyp
|
|
|
|
---
|
|
|
|
### 3. Tab-Struktur (OETO-Standard)
|
|
|
|
#### Veranstaltungs-Tabs (Neue Veranstaltung)
|
|
|
|
Bei einer **neuen Veranstaltung** sind alle 5 Tabs sichtbar:
|
|
|
|
1. **Veranstaltung - Übersicht** (ehemals "Transfer")
|
|
2. **Stammdaten** (A-Satz) ← Standardtab beim Erstellen
|
|
3. **Organisation** (Funktionäre + Plätze)
|
|
4. **Bewerbe** (wird versteckt, da turnierspezifisch)
|
|
5. **Preisliste**
|
|
|
|
#### Veranstaltungs-Tabs (Bestehende Veranstaltung)
|
|
|
|
Bei einer **bestehenden Veranstaltung** wird nur der Übersicht-Tab angezeigt:
|
|
|
|
1. **Veranstaltung - Übersicht**
|
|
|
|
**Grund**: Turnierspezifische Daten (Stammdaten, Organisation, Bewerbe, Preisliste) werden nur auf Turnier-Ebene
|
|
bearbeitet.
|
|
|
|
#### Turnier-Tabs
|
|
|
|
Wenn ein Turnier geöffnet wird, sind alle 5 Tabs sichtbar:
|
|
|
|
1. **Veranstaltung - Übersicht** (Read-only, zeigt Veranstaltungs-Info)
|
|
2. **Stammdaten** (A-Satz)
|
|
3. **Organisation** (Funktionäre + Plätze)
|
|
4. **Bewerbe** ⭐ **Wichtigste Seite der Anwendung**
|
|
5. **Preisliste**
|
|
|
|
---
|
|
|
|
### 4. Bewerbe-Tab - Die Hauptseite
|
|
|
|
Der **Bewerbe-Tab** ist die zentrale Konfigurationsseite des gesamten Systems. Er ist in 3 Bereiche aufgeteilt:
|
|
|
|
```
|
|
┌─────────────┬───────────────────────┬───────────────────────┐
|
|
│ Aktionen │ Bewerbs-Übersicht │ Bewerb-Konfiguration │
|
|
│ (150px) │ (50%) │ (50%) │
|
|
└─────────────┴───────────────────────┴──────────────────────┘
|
|
```
|
|
|
|
#### Links: Aktionen (150px Sidebar)
|
|
|
|
Buttons für Bewerbs-Management:
|
|
|
|
- **Änderungen Speichern** / **Änderungen Rückgängig**
|
|
- **Bewerb Einfügen** / **Bewerb Löschen** / **Bewerb Teilen**
|
|
- **Bewerb nach oben/unten verschieben**
|
|
- **Startliste Bearbeiten** / **Startliste Drucken**
|
|
- **Ergebnisliste Bearbeiten** / **Ergebnisliste Drucken**
|
|
|
|
#### Mitte: Bewerbs-Übersicht (50%)
|
|
|
|
**Toolbar**:
|
|
|
|
- Button: Aktualisieren
|
|
- Button: X Bewerbe (zeigt Anzahl)
|
|
- Button: Filtern
|
|
|
|
**Tabelle** mit folgenden Spalten:
|
|
|
|
- **Tag** (Datum)
|
|
- **Platz** (Platz-Nummer)
|
|
- **Bewerb** (Bewerb-Nummer)
|
|
- **Beginn** (Uhrzeit)
|
|
- **Ende** (Uhrzeit)
|
|
- **Bewerbname** (mehrzeilig möglich)
|
|
- **ZNS** (Zusätzliche Nennung Startnummer)
|
|
- **Nennungen** (Anzahl Anmeldungen)
|
|
|
|
**Features**:
|
|
|
|
- Klickbare Zeilen zur Auswahl
|
|
- Hervorhebung: Bewerbe 5 & 6 haben gelben Hintergrund (`warning.50`)
|
|
- Selected State: Blau/Gelb-Orange je nach Bewerb
|
|
|
|
#### Rechts: Bewerb-Konfiguration (50%)
|
|
|
|
**4 Tabs** zur detaillierten Bewerbs-Konfiguration:
|
|
|
|
##### Tab 1: Bewerb (Grunddaten)
|
|
|
|
- Nummer
|
|
- Abteilung
|
|
- Typ (z.B. "Dressur")
|
|
- Name (z.B. "Dressurreiterprüfung")
|
|
- Bezeichnung (z.B. "Dressurreiterprüfung Reiterpass")
|
|
- Kategorie (Dropdown)
|
|
- Klasse (Dropdown)
|
|
- Lizenz (Dropdown)
|
|
- Maximal (Pferde je Reiter)
|
|
- Pferdealter (Dropdown)
|
|
- Zeile 1, 2, 3 (Zusatzinformationen wie "Pony Einsteiger Cup OÖ")
|
|
- Logo Bewerb (Dateipfad mit "..."-Button)
|
|
|
|
##### Tab 2: Bewertung
|
|
|
|
- Prüfung (z.B. "Dressurreiterprüfung")
|
|
- Richtverfahren (z.B. "A")
|
|
- Para-Grade
|
|
- Richteranzahl
|
|
- Aufgabe (z.B. "Aufgabe R")
|
|
- Aufgabennummer
|
|
- Maximalpunkte (Punkte je Richter)
|
|
|
|
**Richter-Liste**:
|
|
|
|
- Position (z.B. "C")
|
|
- Name (z.B. "Schuster Alexandra")
|
|
- Aktiv (Checkbox)
|
|
|
|
##### Tab 3: Geldpreise
|
|
|
|
**Section: Geldpreis**
|
|
|
|
- Checkbox: Geldpreis
|
|
- Startgeld (z.B. "15,00")
|
|
- Auszahlung (Dropdown: fortführend, 1/3, 1/4, 1/5)
|
|
|
|
**Section: Geldpreis für Kadererreiter**
|
|
|
|
- Checkbox: Geldpreis für Kadererreiter
|
|
- Startgeld für Kadererreiter (z.B. "15,00")
|
|
|
|
**Geldpreisvorlage wählen** (Dropdown)
|
|
|
|
**Tabelle: Geldpreise**
|
|
|
|
- Spalten: Nummer, Geldpreis
|
|
- Zeigt Anzahl der Geldpreise
|
|
|
|
##### Tab 4: Ort/Zeit
|
|
|
|
- Tag (Dropdown: Datum)
|
|
- Beginnzeit (Dropdown: "fix um", "nicht vor", "ca.")
|
|
- Zeit (Textfeld mit Format hh:mm)
|
|
- Reitdauer (Textfeld mit Format mm:ss)
|
|
- Umbau (Textfeld in Minuten)
|
|
- Besichtigung (Textfeld in Minuten)
|
|
- Stechen (Textfeld in Minuten)
|
|
- Platz (Dropdown: "Vorderer Turnierplatz", "Hauptplatz", etc.)
|
|
|
|
---
|
|
|
|
## Datenstrukturen
|
|
|
|
### Bewerb Interface
|
|
|
|
```typescript
|
|
interface Bewerb {
|
|
id: number;
|
|
tag: string; // Tabellen-Datum
|
|
platz: number; // Platz-Nummer
|
|
bewerb: number; // Bewerb-Nummer
|
|
beginn: string; // Beginn-Zeit
|
|
ende: string; // End-Zeit
|
|
bewerbname: string; // Mehrzeiliger Name
|
|
zns: number; // ZNS
|
|
nennungen: number; // Anzahl Nennungen
|
|
|
|
// Tab 1: Bewerb
|
|
nummer: string;
|
|
abteilung: string;
|
|
typ: string;
|
|
name: string;
|
|
bezeichnung: string;
|
|
kategorie: string;
|
|
klasse: string;
|
|
lizenz: string;
|
|
maximal: string;
|
|
pferdealter: string;
|
|
zeile1: string;
|
|
zeile2: string;
|
|
zeile3: string;
|
|
logoBewerbPfad: string;
|
|
|
|
// Tab 2: Bewertung
|
|
prufung: string;
|
|
richtverfahren: string;
|
|
paraGrade: string;
|
|
richteranzahl: number;
|
|
aufgabe: string;
|
|
aufgabennr: string;
|
|
maximalPunkte: string;
|
|
richter: {
|
|
position: string;
|
|
name: string;
|
|
aktiv: boolean;
|
|
}[];
|
|
|
|
// Tab 3: Geldpreise
|
|
geldpreisAktiv: boolean;
|
|
startgeld: string;
|
|
auszahlung: string;
|
|
geldpreisKadererreiterAktiv: boolean;
|
|
startgeldKadererreiter: string;
|
|
geldpreisvorlage: string;
|
|
geldpreise: {
|
|
nummer: string;
|
|
betrag: string;
|
|
}[];
|
|
|
|
// Tab 4: Ort/Zeit
|
|
tagDatum: string;
|
|
beginnzeit: string;
|
|
beginnZeit: string;
|
|
reitdauer: string;
|
|
umbau: string;
|
|
besichtigung: string;
|
|
stechen: string;
|
|
platzName: string;
|
|
}
|
|
```
|
|
|
|
### Veranstaltung Interface
|
|
|
|
```typescript
|
|
interface Veranstaltung {
|
|
id: string;
|
|
name: string;
|
|
von: string; // Datum von
|
|
bis: string; // Datum bis
|
|
ort: string;
|
|
status: string;
|
|
turniere: Turnier[];
|
|
}
|
|
```
|
|
|
|
### Turnier Interface
|
|
|
|
```typescript
|
|
interface Turnier {
|
|
nr: number;
|
|
name: string;
|
|
datum: string;
|
|
status: string;
|
|
bewerbe: Bewerb[];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Design-System
|
|
|
|
### Farbschema (Material Design 3)
|
|
|
|
**Primärfarbe**: Indigo (#3F51B5)
|
|
|
|
```css
|
|
/* Theme Colors (src/styles/theme.css) */
|
|
--primary-color: #3F51B5;
|
|
--primary-light: #757DE8;
|
|
--primary-dark: #002984;
|
|
|
|
/* Semantic Colors */
|
|
--background-default: #FAFAFA;
|
|
--background-paper: #FFFFFF;
|
|
--text-primary: rgba(0, 0, 0, 0.87);
|
|
--text-secondary: rgba(0, 0, 0, 0.60);
|
|
--divider: rgba(0, 0, 0, 0.12);
|
|
|
|
/* Status Colors */
|
|
--success-color: #4CAF50;
|
|
--warning-color: #FF9800;
|
|
--error-color: #F44336;
|
|
--info-color: #2196F3;
|
|
```
|
|
|
|
### Typografie
|
|
|
|
- **Body Text**: 10px - 11px (sehr kompakt für Desktop)
|
|
- **Labels**: 10px, 600 Font Weight
|
|
- **Section Headers**: 11px - 13px, 600 Font Weight
|
|
- **Schriftart**: System Fonts (Roboto via MUI)
|
|
|
|
### Spacing & Layout
|
|
|
|
- **Kompakte Abstände**: 1-2 (8px - 16px)
|
|
- **Form-Felder**:
|
|
- Höhe: `small` size
|
|
- Padding: `py: 0.5` (4px)
|
|
- Font: 10px
|
|
- **Sidebar Width**: 150px (Aktionen-Sidebar im Bewerbe-Tab)
|
|
- **Drawer Width**: 280px (Haupt-Navigation)
|
|
|
|
### Component-Sizing
|
|
|
|
```typescript
|
|
// Standardgrößen
|
|
size="small" // Buttons, TextFields, Selects
|
|
sx={{ fontSize: '10px' }} // Text
|
|
sx={{ py: 0.5 }} // Input Padding
|
|
sx={{ gap: 1 }} // 8px Abstand
|
|
sx={{ gap: 1.5 }} // 12px Abstand
|
|
```
|
|
|
|
---
|
|
|
|
## MUI Theme Konfiguration
|
|
|
|
Die Anwendung verwendet MUI's Default Theme mit angepasster Primärfarbe:
|
|
|
|
```typescript
|
|
// src/main.tsx
|
|
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
|
|
|
const theme = createTheme({
|
|
palette: {
|
|
primary: {
|
|
main: '#3F51B5', // Indigo
|
|
},
|
|
},
|
|
components: {
|
|
MuiButton: {
|
|
styleOverrides: {
|
|
root: {
|
|
textTransform: 'none', // Keine Großbuchstaben
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## State Management
|
|
|
|
### Aktuelle Implementierung (Prototyp)
|
|
|
|
Der Prototyp verwendet **React Local State** mit `useState`:
|
|
|
|
```typescript
|
|
// Beispiel: BewerbeTab.tsx
|
|
const [bewerbe, setBewerbe] = useState<Bewerb[]>(mockBewerbe);
|
|
const [selectedBewerbId, setSelectedBewerbId] = useState<number>(1);
|
|
const [detailTab, setDetailTab] = useState(0);
|
|
```
|
|
|
|
### Empfehlung für Production
|
|
|
|
Für die Production-Version empfehlen wir:
|
|
|
|
1. **React Context API** für globalen State (Login, aktuelle Veranstaltung/Turnier)
|
|
2. **Zustand** oder **Redux Toolkit** für komplexes State Management
|
|
3. **React Query** für Server-State und Caching
|
|
4. **localStorage/sessionStorage** für Persistenz
|
|
|
|
Beispiel mit React Context:
|
|
|
|
```typescript
|
|
// context/VeranstaltungContext.tsx
|
|
const VeranstaltungContext = createContext<VeranstaltungContextType | null>(null);
|
|
|
|
export function VeranstaltungProvider({ children }: { children: ReactNode }) {
|
|
const [activeVeranstaltung, setActiveVeranstaltung] = useState<Veranstaltung | null>(null);
|
|
const [activeTurnier, setActiveTurnier] = useState<Turnier | null>(null);
|
|
|
|
return (
|
|
<VeranstaltungContext.Provider value={{
|
|
activeVeranstaltung,
|
|
setActiveVeranstaltung,
|
|
activeTurnier,
|
|
setActiveTurnier
|
|
}}>
|
|
{children}
|
|
</VeranstaltungContext.Provider>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Backend-Integration (TODO)
|
|
|
|
### API Endpunkte (geplant)
|
|
|
|
```typescript
|
|
// Veranstaltungen
|
|
GET /api/veranstaltungen
|
|
GET /api/veranstaltungen/:id
|
|
POST /api/veranstaltungen
|
|
PUT /api/veranstaltungen/:id
|
|
DELETE /api/veranstaltungen/:id
|
|
|
|
// Turniere
|
|
GET /api/veranstaltungen/:veranstaltungId/turniere
|
|
GET /api/veranstaltungen/:veranstaltungId/turniere/:nr
|
|
POST /api/veranstaltungen/:veranstaltungId/turniere
|
|
PUT /api/veranstaltungen/:veranstaltungId/turniere/:nr
|
|
DELETE /api/veranstaltungen/:veranstaltungId/turniere/:nr
|
|
|
|
// Bewerbe
|
|
GET /api/turniere/:turnierId/bewerbe
|
|
GET /api/turniere/:turnierId/bewerbe/:id
|
|
POST /api/turniere/:turnierId/bewerbe
|
|
PUT /api/turniere/:turnierId/bewerbe/:id
|
|
DELETE /api/turniere/:turnierId/bewerbe/:id
|
|
|
|
// ÖPS Datasourcing
|
|
POST /api/ops/import/veranstaltung/:id
|
|
POST /api/ops/import/turnier/:id
|
|
```
|
|
|
|
### Authentifizierung
|
|
|
|
```typescript
|
|
POST /api/auth/login
|
|
POST /api/auth/logout
|
|
GET /api/auth/me
|
|
POST /api/auth/refresh
|
|
```
|
|
|
|
---
|
|
|
|
## Entwicklungsrichtlinien
|
|
|
|
### Code Style
|
|
|
|
1. **TypeScript Strict Mode**: Aktiviert
|
|
2. **Naming Conventions**:
|
|
- Components: PascalCase (z.B. `BewerbeTab.tsx`)
|
|
- Functions: camelCase (z.B. `handleBewerbAendern`)
|
|
- Interfaces: PascalCase (z.B. `Bewerb`)
|
|
- CSS Classes: kebab-case (falls verwendet)
|
|
|
|
3. **Component Structure**:
|
|
|
|
```typescript
|
|
// 1. Imports
|
|
import React from 'react';
|
|
import { Box, Button } from '@mui/material';
|
|
|
|
// 2. Interfaces/Types
|
|
interface Props { ... }
|
|
|
|
// 3. Component
|
|
export function ComponentName({ prop1, prop2 }: Props) {
|
|
// 3.1 State
|
|
const [state, setState] = useState();
|
|
|
|
// 3.2 Handlers
|
|
const handleAction = () => { ... };
|
|
|
|
// 3.3 Effects
|
|
useEffect(() => { ... }, []);
|
|
|
|
// 3.4 Render
|
|
return ( ... );
|
|
}
|
|
```
|
|
|
|
### MUI Best Practices
|
|
|
|
1. **Sx Props bevorzugen** statt styled components:
|
|
|
|
```typescript
|
|
// ✅ Gut
|
|
<Box sx={{ p: 2, bgcolor: 'primary.main' }}>
|
|
|
|
// ❌ Vermeiden (im Prototyp)
|
|
<StyledBox>
|
|
```
|
|
|
|
2. **Theme-basierte Werte verwenden**:
|
|
|
|
```typescript
|
|
// ✅ Gut - Theme Colors
|
|
sx={{ color: 'primary.main', bgcolor: 'grey.50' }}
|
|
|
|
// ❌ Vermeiden - Hardcoded
|
|
sx={{ color: '#3F51B5', bgcolor: '#FAFAFA' }}
|
|
```
|
|
|
|
3. **Responsive Werte** (für spätere mobile Version):
|
|
|
|
```typescript
|
|
sx={{
|
|
width: { xs: '100%', md: 300 },
|
|
display: { xs: 'none', md: 'block' }
|
|
}}
|
|
```
|
|
|
|
### Performance-Optimierung
|
|
|
|
1. **React.memo** für große Listen:
|
|
|
|
```typescript
|
|
export const BewerbRow = React.memo(({ bewerb }: Props) => { ... });
|
|
```
|
|
|
|
2. **useCallback** für Event Handlers in Listen:
|
|
|
|
```typescript
|
|
const handleSelect = useCallback((id: number) => { ... }, []);
|
|
```
|
|
|
|
3. **Lazy Loading** für Tabs:
|
|
|
|
```typescript
|
|
const BewerbeTab = lazy(() => import('./turnier/BewerbeTab'));
|
|
```
|
|
|
|
---
|
|
|
|
## Testing (geplant)
|
|
|
|
### Unit Tests mit Vitest
|
|
|
|
```typescript
|
|
// BewerbeTab.test.tsx
|
|
import { render, screen } from '@testing-library/react';
|
|
import { BewerbeTab } from './BewerbeTab';
|
|
|
|
describe('BewerbeTab', () => {
|
|
it('renders 12 bewerbe', () => {
|
|
render(<BewerbeTab />);
|
|
expect(screen.getByText('12 Bewerbe')).toBeInTheDocument();
|
|
});
|
|
});
|
|
```
|
|
|
|
### E2E Tests mit Playwright
|
|
|
|
```typescript
|
|
// e2e/bewerbe.spec.ts
|
|
test('can create new bewerb', async ({ page }) => {
|
|
await page.goto('/veranstaltung/1/turnier/1');
|
|
await page.click('text=Bewerb Einfügen');
|
|
await page.fill('input[name="nummer"]', '13');
|
|
// ...
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Browser-Unterstützung
|
|
|
|
**Ziel-Browser** (Desktop):
|
|
|
|
- Chrome/Edge >= 90
|
|
- Firefox >= 88
|
|
- Safari >= 14
|
|
|
|
**NICHT unterstützt**:
|
|
|
|
- Internet Explorer
|
|
- Mobile Browser (vorerst)
|
|
|
|
---
|
|
|
|
## Bekannte Einschränkungen (Prototyp)
|
|
|
|
1. **Keine Backend-Integration**: Alle Daten sind Mock-Daten
|
|
2. **Keine Persistenz**: Änderungen gehen bei Page Refresh verloren
|
|
3. **Eingeschränkte Validierung**: Minimale Form-Validierung
|
|
4. **Keine Fehlerbehandlung**: Fehler-States nicht implementiert
|
|
5. **Mock-Login**: Demo-Credentials hart-kodiert
|
|
6. **Keine Exports**: Drucken/Exportieren nur als Placeholder-Buttons
|
|
7. **Keine Suche/Filter**: Filter-Funktionen nicht implementiert
|
|
8. **Keine Undo/Redo**: "Änderungen Rückgängig" nicht funktional
|
|
|
|
---
|
|
|
|
## Nächste Schritte / Roadmap
|
|
|
|
### Phase 1: Backend-Integration
|
|
|
|
- [ ] REST API Implementation
|
|
- [ ] Authentifizierungs-System
|
|
- [ ] Datenbank-Schema (PostgreSQL empfohlen)
|
|
- [ ] ÖPS Datasourcing API-Integration
|
|
|
|
### Phase 2: Erweiterte Features
|
|
|
|
- [ ] Such- und Filter-Funktionen
|
|
- [ ] Sortierung in Tabellen
|
|
- [ ] Drag & Drop für Bewerbs-Reihenfolge
|
|
- [ ] Bulk-Operations (mehrere Bewerbe gleichzeitig bearbeiten)
|
|
- [ ] Undo/Redo-Funktionalität
|
|
- [ ] Auto-Save (mit Debouncing)
|
|
|
|
### Phase 3: Export & Reporting
|
|
|
|
- [ ] PDF-Export (Startlisten, Ergebnislisten)
|
|
- [ ] Excel-Export
|
|
- [ ] Druckvorlagen
|
|
- [ ] Berichts-Templates
|
|
|
|
### Phase 4: Erweiterte Tabs
|
|
|
|
- [ ] Organisation-Tab: Funktionäre-Verwaltung
|
|
- [ ] Organisation-Tab: Plätze-Verwaltung
|
|
- [ ] Preisliste-Tab: Vollständige Implementierung
|
|
- [ ] Übersicht-Tab: Dashboard mit Statistiken
|
|
|
|
### Phase 5: Zusätzliche Module
|
|
|
|
- [ ] Meisterschaften/Cups-Verwaltung
|
|
- [ ] Nennungs-System
|
|
- [ ] Starter-Verwaltung
|
|
- [ ] Pferde-Datenbank
|
|
- [ ] Reiter-Datenbank
|
|
|
|
### Phase 6: Polish & Optimierung
|
|
|
|
- [ ] Umfassendes Testing
|
|
- [ ] Performance-Optimierung
|
|
- [ ] Accessibility (WCAG 2.1 AA)
|
|
- [ ] Internationalisierung (i18n)
|
|
- [ ] Keyboard Shortcuts
|
|
- [ ] Offline-Modus (PWA)
|
|
|
|
---
|
|
|
|
## Häufige Entwicklungs-Aufgaben
|
|
|
|
### Neue Komponente hinzufügen
|
|
|
|
```typescript
|
|
// src/app/components/MyComponent.tsx
|
|
import { Box, Typography } from '@mui/material';
|
|
|
|
interface MyComponentProps {
|
|
title: string;
|
|
}
|
|
|
|
export function MyComponent({ title }: MyComponentProps) {
|
|
return (
|
|
<Box sx={{ p: 2 }}>
|
|
<Typography variant="h6" sx={{ fontSize: '13px', fontWeight: 600 }}>
|
|
{title}
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Neue Route hinzufügen
|
|
|
|
```typescript
|
|
// src/app/routes.tsx
|
|
{
|
|
path: "my-new-page",
|
|
Component: MyNewPage,
|
|
}
|
|
```
|
|
|
|
### Neuen Tab in Veranstaltung/Turnier hinzufügen
|
|
|
|
```typescript
|
|
// In VeranstaltungAnsicht.tsx oder TurnierAnsicht.tsx
|
|
const tabs = [
|
|
// ... bestehende Tabs
|
|
{ label: 'Mein neuer Tab', component: <MyNewTabComponent /> }
|
|
];
|
|
```
|
|
|
|
### MUI Component anpassen
|
|
|
|
```typescript
|
|
// Global Theme Override
|
|
const theme = createTheme({
|
|
components: {
|
|
MuiButton: {
|
|
styleOverrides: {
|
|
root: {
|
|
textTransform: 'none',
|
|
fontSize: '10px',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
// Oder mit Sx Props
|
|
<Button sx={{ textTransform: 'none', fontSize: '10px' }}>
|
|
```
|
|
|
|
---
|
|
|
|
## Debugging
|
|
|
|
### React DevTools
|
|
|
|
Installieren Sie die React DevTools Browser Extension:
|
|
|
|
- [Chrome](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)
|
|
- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/react-devtools/)
|
|
|
|
### Vite Development Server
|
|
|
|
Der Dev Server unterstützt:
|
|
|
|
- **Hot Module Replacement (HMR)**: Änderungen werden sofort sichtbar
|
|
- **Error Overlay**: Fehler werden im Browser angezeigt
|
|
- **Source Maps**: Original TypeScript Code im Browser sichtbar
|
|
|
|
### Console Logging
|
|
|
|
```typescript
|
|
// Beispiel in BewerbeTab.tsx
|
|
const handleSpeichern = () => {
|
|
console.log('Bewerbe speichern:', bewerbe);
|
|
};
|
|
```
|
|
|
|
### Redux DevTools (für zukünftige Implementierung)
|
|
|
|
Wenn State Management mit Redux/Zustand implementiert wird:
|
|
|
|
- [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools)
|
|
|
|
---
|
|
|
|
## Deployment (geplant)
|
|
|
|
### Production Build
|
|
|
|
```bash
|
|
pnpm build
|
|
```
|
|
|
|
Output: `dist/` Ordner
|
|
|
|
### Hosting-Optionen
|
|
|
|
1. **Statisches Hosting** (für Frontend-Only):
|
|
- Vercel
|
|
- Netlify
|
|
- AWS S3 + CloudFront
|
|
- Azure Static Web Apps
|
|
|
|
2. **Full-Stack Hosting** (mit Backend):
|
|
- AWS (EC2, ECS, Lambda)
|
|
- Azure App Service
|
|
- Google Cloud Run
|
|
- DigitalOcean App Platform
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# .env.production
|
|
VITE_API_URL=https://api.turnierverwaltung.at
|
|
VITE_OPS_API_URL=https://ops-api.example.at
|
|
```
|
|
|
|
Zugriff im Code:
|
|
|
|
```typescript
|
|
const API_URL = import.meta.env.VITE_API_URL;
|
|
```
|
|
|
|
---
|
|
|
|
## Support & Kontakt
|
|
|
|
### Dokumentation
|
|
|
|
- **React**: https://react.dev
|
|
- **React Router**: https://reactrouter.com
|
|
- **Material-UI**: https://mui.com
|
|
- **TypeScript**: https://www.typescriptlang.org
|
|
- **Vite**: https://vitejs.dev
|
|
|
|
### Code-Review Checkliste
|
|
|
|
Vor dem Commit prüfen:
|
|
|
|
- [ ] TypeScript-Fehler behoben (`pnpm run type-check`)
|
|
- [ ] Code formatiert (Prettier/ESLint)
|
|
- [ ] Console.log() Statements entfernt
|
|
- [ ] Keine hardcoded URLs/Credentials
|
|
- [ ] Responsive Design getestet (falls relevant)
|
|
- [ ] Browser-Kompatibilität geprüft
|
|
- [ ] Performance: Keine unnötigen Re-Renders
|
|
|
|
---
|
|
|
|
## Glossar
|
|
|
|
**Begriffe aus dem Pferdesport / OETO-Standard**:
|
|
|
|
- **OETO**: Österreichischer Turnierordnung
|
|
- **ÖPS**: Österreichischer Pferdesportverband
|
|
- **A-Satz**: Stammdaten einer Veranstaltung/Turniers
|
|
- **Bewerb**: Einzelne Prüfung innerhalb eines Turniers
|
|
- **Richtverfahren**: Bewertungsmethode (A, C, E)
|
|
- **Para-Grade**: Klassifizierung für Para-Dressur
|
|
- **ZNS**: Zusätzliche Nennung Startnummer
|
|
- **Kadererreiter**: Reiter im Nationalmannschafts-Kader
|
|
- **Führzügelklasse**: Wettbewerb für sehr junge Reiter
|
|
- **First Ridden**: Englische Bezeichnung für Einsteiger-Wettbewerb
|
|
- **Stechen**: Zusätzliche Runde zur Entscheidung bei Gleichstand
|
|
|
|
---
|
|
|
|
## Lizenz
|
|
|
|
© 2024 - Internes Projekt für [Organisation]
|
|
|
|
---
|
|
|
|
## Changelog
|
|
|
|
### Version 1.0 (Prototyp) - 2026-03-24
|
|
|
|
**Neue Features dieser Session**:
|
|
|
|
- ✅ **Abrechnung-Tab**: Vollständige Bar-Zahlungs-Funktionalität
|
|
- Buchungstabelle mit Soll/Haben/Saldo
|
|
- Teilnehmer-Auswahl (Reiter/Pferd)
|
|
- Zahlungsarten: Bar, Scheck (+30€), Bankomat, Kreditkarte
|
|
- Direkt-Druck für Saldo & Rechnung
|
|
- ✅ **Nennungen-Tab**: Desktop-Nennungs-Maske integriert
|
|
- Pferd & Reiter Eingabe mit Cross-Reference
|
|
- Nennungen-Tabelle mit 3 Tabs
|
|
- Verkauf/Buchungen Panel
|
|
- Bewerbsliste mit Doppelklick-Nennung
|
|
- ✅ **8 Turnier-Tabs**: Vollständige Tab-Struktur
|
|
- Stammdaten → Organisation → Bewerbe → Artikel → Abrechnung → Nennungen → Startlisten → Ergebnislisten
|
|
- ✅ **Umfassende Dokumentation**:
|
|
- `/docs/README.md` - Haupt-Dokumentation
|
|
- `/docs/ARCHITECTURE.md` - Für Lead-Architekt
|
|
- `/docs/BACKEND.md` - Für Backend Developer
|
|
- `/docs/FRONTEND.md` - Für Frontend Developer
|
|
- `/docs/UI-UX.md` - Für UI/UX Designer
|
|
- `/docs/CHANGELOG.md` - Kompletter Chat-Verlauf
|
|
|
|
### Version 0.1.0 (Prototyp) - 2024-03-24
|
|
|
|
**Initiale Features**:
|
|
|
|
- ✅ Login-System (Demo-Credentials)
|
|
- ✅ Haupt-Navigation (AdminDrawer)
|
|
- ✅ Veranstaltungs-Verwaltung
|
|
- ✅ Turnier-Verwaltung
|
|
- ✅ Bewerbe-Tab (vollständig)
|
|
- Tabellen-Übersicht (12 Mock-Bewerbe)
|
|
- 4 Konfigurations-Tabs (Bewerb, Bewertung, Geldpreise, Ort/Zeit)
|
|
- Aktions-Buttons
|
|
- ✅ Routing-System (React Router Data Mode)
|
|
- ✅ Material Design 3 UI (Indigo Theme)
|
|
- ✅ Desktop-optimierte Layouts
|
|
- ✅ Tab-Struktur nach OETO-Standard
|
|
|
|
**Bekannte Issues**:
|
|
|
|
- Keine Backend-Integration
|
|
- Mock-Daten only
|
|
- Limitierte Interaktivität
|
|
|
|
---
|
|
|
|
**Hinweis für Entwickler**: Dies ist ein **Prototyp** zur Demonstration der UI/UX. Die vollständige Implementierung
|
|
erfordert Backend-Integration, Datenpersistenz, Validierung und erweiterte Features gemäß der Roadmap.
|
|
|
|
Bei Fragen kontaktieren Sie bitte [Projektleiter/Tech Lead].
|