CG-Feedback-Monitor/src/App.tsx
Lars d894c3c8e4 V2-Stand: UX-Dashboard, Personen v2, Starter-Paket und Dexie-Legacy entfernt.
Sichert den lokalen Entwicklungsstand nach Gitea: Nav/Dashboard, Personen-Excel/Archiv, Team-Setup, Assignment-Übergabe und C6/C7/T2.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 14:17:25 +02:00

120 lines
5.1 KiB
TypeScript

import { useEffect } from 'react'
import { Routes, Route, NavLink, Navigate, useLocation } from 'react-router-dom'
import Dashboard from './pages/Dashboard'
import Configuration from './pages/Configuration'
import Consultants from './pages/Consultants'
import AssignmentCreate from './pages/AssignmentCreate'
import AssignmentDetail from './pages/AssignmentDetail'
import AssignmentEdit from './pages/AssignmentEdit'
import AssignmentTrash from './pages/AssignmentTrash'
import AssignmentArchive from './pages/AssignmentArchive'
import MeetingHistory from './pages/MeetingHistory'
import MeetingView from './pages/meeting/MeetingView'
import FeedbackDraftPage from './pages/FeedbackDraftPage'
import AssignmentFeedbackPage from './pages/AssignmentFeedbackPage'
import Evaluation from './pages/Evaluation'
import ConsultantDetailPage from './pages/ConsultantDetail'
function headerContext(pathname: string): string {
if (pathname.startsWith('/config')) return 'Einstellungen'
if (pathname.startsWith('/consultants')) return 'Personen'
if (pathname.startsWith('/evaluation') || pathname.startsWith('/feedbacks')) return 'Feedbacks'
if (pathname.startsWith('/assignments/trash')) return 'Papierkorb'
if (pathname.startsWith('/assignments/archive')) return 'Archiv'
if (/\/meeting\//.test(pathname)) return 'Meeting'
if (/\/history$/.test(pathname)) return 'Gesamtprotokoll'
if (/\/feedback/.test(pathname)) return 'Feedback'
if (pathname.startsWith('/assignment')) return 'Assignment'
return 'Assignments'
}
const NAV = [
{ to: '/', label: 'Assignments', end: true },
{ to: '/feedbacks', label: 'Feedbacks', end: false },
{ to: '/consultants', label: 'Personen', end: false },
{ to: '/config', label: 'Einstellungen', end: false },
] as const
export default function App() {
const { pathname } = useLocation()
const wideLayout =
pathname === '/' ||
pathname.startsWith('/feedbacks') ||
pathname.startsWith('/evaluation') ||
pathname.startsWith('/consultants') ||
/\/meeting\/|\/history$/.test(pathname)
const context = headerContext(pathname)
useEffect(() => {
document.title = context === 'Assignments'
? 'Assignment Monitor'
: `${context} · Assignment Monitor`
}, [context])
return (
<div className="min-h-dvh bg-app flex flex-col font-sans text-ink">
<header className="bg-brand text-white px-4 py-3 flex items-center gap-3 shrink-0 shadow-[var(--shadow-header)]">
<div className="min-w-0 flex-1">
<div className="font-bold text-lg tracking-tight leading-tight truncate">
Assignment Monitor
</div>
<div className="text-xs text-white/80 mt-0.5 truncate">{context}</div>
</div>
</header>
<main
className={`flex-1 w-full mx-auto px-4 pt-4 pb-24 ${
wideLayout ? 'max-w-6xl' : 'max-w-2xl'
}`}
>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/assignment/new" element={<AssignmentCreate />} />
<Route path="/assignments/trash" element={<AssignmentTrash />} />
<Route path="/assignments/archive" element={<AssignmentArchive />} />
<Route path="/assignment/:id" element={<AssignmentDetail />} />
<Route path="/assignment/:id/edit" element={<AssignmentEdit />} />
<Route path="/assignment/:id/history" element={<MeetingHistory />} />
<Route path="/assignment/:id/meeting/:meetingId" element={<MeetingView />} />
<Route path="/assignment/:id/feedback/:instituteeId" element={<AssignmentFeedbackPage />} />
<Route path="/assignment/:id/feedback-draft/:instituteeId" element={<FeedbackDraftPage />} />
<Route path="/feedbacks" element={<Evaluation />} />
<Route path="/evaluation" element={<Navigate to="/feedbacks" replace />} />
<Route path="/consultants" element={<Consultants />} />
<Route path="/consultants/:id" element={<ConsultantDetailPage />} />
<Route path="/config" element={<Configuration />} />
</Routes>
</main>
<nav
className="fixed bottom-0 inset-x-0 z-20 bg-surface border-t border-line shadow-[var(--shadow-nav)]"
aria-label="Hauptnavigation"
>
<div className={`mx-auto flex justify-around px-1 pt-1 pb-[max(0.35rem,env(safe-area-inset-bottom))] ${
wideLayout ? 'max-w-6xl' : 'max-w-2xl'
}`}>
{NAV.map(({ to, label, end }) => (
<NavLink
key={to}
to={to}
end={end}
className={({ isActive }) =>
[
'flex-1 min-h-11 max-w-[7.5rem] mx-0.5 flex items-center justify-center',
'rounded-[var(--radius-control)] px-2 text-[11px] sm:text-xs font-medium leading-tight text-center',
'transition-colors',
isActive
? 'bg-brand-soft text-brand font-semibold'
: 'text-ink-muted hover:text-ink hover:bg-app',
].join(' ')
}
>
{label}
</NavLink>
))}
</div>
</nav>
</div>
)
}