feat(exercise): Übungsmodus mit Übungs-Cockpit + direkte Einstiegs-Buttons
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 40m38s

- Topbar: direkte Buttons "Neuer Einsatz" (rot) und "Neue Übung" (blau)
  statt versteckt im Menü; öffnen den Dialog direkt im richtigen Modus
- Übungs-Cockpit ersetzt bei Übungen das Journal: Übungsziele mit
  Zielerreichung (Offen/Erreicht/Teilweise/Nicht erreicht) + Auswertung
- Neues Model ExerciseGoal + Project.exerciseEvaluation (Auto-Migration)
- API-Routen /projects/[id]/exercise-goals (Liste/Erstellen/Update/Löschen)
- Deutliches "ÜBUNG"-Banner gegen Verwechslung mit echtem Einsatz
- Sidebar-Tab heisst bei Übung "Auswertung"
- Version 1.4.4

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-18 21:16:32 +02:00
parent 1641101b73
commit 1c8f619252
13 changed files with 560 additions and 34 deletions

View File

@@ -16,7 +16,8 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import {
Plus,
Flame,
GraduationCap,
Save,
FolderOpen,
Download,
@@ -43,13 +44,13 @@ import {
Unlock,
} from 'lucide-react'
import { HoseSettingsDialog } from '@/components/dialogs/hose-settings-dialog'
import type { Project, DrawFeature } from '@/types'
import type { Project, DrawFeature, ProjectMode } from '@/types'
import { formatDateTime } from '@/lib/utils'
import { Logo } from '@/components/ui/logo'
interface TopbarProps {
project: Project | null
onNewProject: () => void
onNewProject: (mode?: ProjectMode) => void
onSaveProject: () => void
onLoadProject: (project: Project, features: DrawFeature[]) => void
onDeleteProject?: (projectId: string) => void
@@ -188,6 +189,26 @@ export function Topbar({
<span className="hidden lg:inline">{presentationLocked ? 'Gesperrt' : 'Frei'}</span>
</Button>
{/* Direkte Einstiegspunkte: Einsatz (rot) und Übung (blau) */}
<Button
data-tour="new-project"
onClick={() => onNewProject('EINSATZ')}
className="h-9 md:h-10 px-2 md:px-3 text-sm bg-red-600 hover:bg-red-700 text-white"
title="Neuen Einsatz erstellen"
>
<Flame className="w-5 h-5 md:mr-1" />
<span className="hidden lg:inline">Neuer Einsatz</span>
</Button>
<Button
onClick={() => onNewProject('UEBUNG')}
variant="outline"
className="h-9 md:h-10 px-2 md:px-3 text-sm border-blue-500 text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-950/40"
title="Neue Übung erstellen"
>
<GraduationCap className="w-5 h-5 md:mr-1" />
<span className="hidden lg:inline">Neue Übung</span>
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="h-9 md:h-10 px-2 md:px-3 text-sm" title="Menü">
@@ -196,10 +217,6 @@ export function Topbar({
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-52">
<DropdownMenuItem data-tour="new-project" onClick={onNewProject} className="py-2.5 px-3">
<Plus className="w-4 h-4 mr-2" />
Neuer Einsatz
</DropdownMenuItem>
<DropdownMenuItem onClick={handleOpenLoadDialog} className="py-2.5 px-3">
<List className="w-4 h-4 mr-2" />
Einsätze verwalten