feat(ui): Dashboard als Startseite für Verantwortliche + einstellbar
CI / backend-tests (push) Successful in 2m0s
CI / frontend-build (push) Successful in 19s

Root-Route "/" und Catch-all landeten bisher immer auf /objekte,
Login-Weiterleitung (Verantwortliche -> Dashboard) war der einzige
Pfad dorthin. Jetzt zentrale startseitenPfad()-Logik überall genutzt,
zusätzlich per Dropdown in der Sidebar einstellbar (automatisch/
Dashboard/Objekte), Präferenz in localStorage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-06 13:37:02 +02:00
co-authored by Claude Sonnet 5
parent ed9ba29581
commit 21ea63a69a
5 changed files with 72 additions and 2 deletions
+9 -1
View File
@@ -11,6 +11,7 @@ import { KontrollPage } from "./pages/KontrollPage";
import { LoginPage } from "./pages/LoginPage"; import { LoginPage } from "./pages/LoginPage";
import { MangelListePage } from "./pages/MangelListePage"; import { MangelListePage } from "./pages/MangelListePage";
import { ObjektListPage } from "./pages/ObjektListPage"; import { ObjektListPage } from "./pages/ObjektListPage";
import { startseitenPfad } from "./settings/startseite";
function GeschuetzteRoute({ children }: { children: ReactNode }) { function GeschuetzteRoute({ children }: { children: ReactNode }) {
const { eingeloggt } = useAuth(); const { eingeloggt } = useAuth();
@@ -20,6 +21,12 @@ function GeschuetzteRoute({ children }: { children: ReactNode }) {
return <AppShell>{children}</AppShell>; return <AppShell>{children}</AppShell>;
} }
function Startseite() {
const { eingeloggt, istVerantwortlich } = useAuth();
if (!eingeloggt) return <Navigate to="/login" replace />;
return <Navigate to={startseitenPfad(istVerantwortlich)} replace />;
}
function AdminRoute({ children }: { children: ReactNode }) { function AdminRoute({ children }: { children: ReactNode }) {
const { eingeloggt, istVerantwortlich } = useAuth(); const { eingeloggt, istVerantwortlich } = useAuth();
if (!eingeloggt) { if (!eingeloggt) {
@@ -39,6 +46,7 @@ export default function App() {
<AuthProvider> <AuthProvider>
<Routes> <Routes>
<Route path="/login" element={<LoginPage />} /> <Route path="/login" element={<LoginPage />} />
<Route path="/" element={<Startseite />} />
<Route <Route
path="/objekte" path="/objekte"
element={ element={
@@ -95,7 +103,7 @@ export default function App() {
</AdminRoute> </AdminRoute>
} }
/> />
<Route path="*" element={<Navigate to="/objekte" replace />} /> <Route path="*" element={<Startseite />} />
</Routes> </Routes>
</AuthProvider> </AuthProvider>
); );
+2
View File
@@ -3,6 +3,7 @@ import { NavLink, useNavigate } from "react-router-dom";
import { useAuth } from "../auth/AuthContext"; import { useAuth } from "../auth/AuthContext";
import { CommandPalette } from "./CommandPalette"; import { CommandPalette } from "./CommandPalette";
import { StartseiteAuswahl } from "./StartseiteAuswahl";
import { SyncStatus } from "./status/SyncStatus"; import { SyncStatus } from "./status/SyncStatus";
import { ThemeToggle } from "./ThemeToggle"; import { ThemeToggle } from "./ThemeToggle";
@@ -48,6 +49,7 @@ export function AppShell({ children }: { children: ReactNode }) {
{istAdmin ? "Administration" : "Verwaltung"} {istAdmin ? "Administration" : "Verwaltung"}
</NavLink> </NavLink>
)} )}
{istVerantwortlich && <StartseiteAuswahl />}
<SyncStatus /> <SyncStatus />
<button className="btn btn-secondary" onClick={paletteOeffnen} title="Globale Suche (Strg/⌘+K)"> <button className="btn btn-secondary" onClick={paletteOeffnen} title="Globale Suche (Strg/⌘+K)">
🔍 Suche 🔍 Suche
@@ -0,0 +1,29 @@
import { useState } from "react";
import type { StartseitePraeferenz } from "../settings/startseite";
import { gespeichertePraeferenz, praeferenzSpeichern } from "../settings/startseite";
/** Nur relevant für Verantwortliche - Mitarbeiter haben ohnehin nur "Objekte". */
export function StartseiteAuswahl() {
const [praeferenz, setPraeferenz] = useState<StartseitePraeferenz>(() => gespeichertePraeferenz());
function aendern(neu: StartseitePraeferenz) {
setPraeferenz(neu);
praeferenzSpeichern(neu);
}
return (
<select
className="input"
style={{ width: "auto" }}
value={praeferenz}
onChange={(e) => aendern(e.target.value as StartseitePraeferenz)}
title="Startseite nach Login"
aria-label="Startseite nach Login"
>
<option value="auto">Startseite: automatisch</option>
<option value="dashboard">Startseite: Dashboard</option>
<option value="objekte">Startseite: Objekte</option>
</select>
);
}
+2 -1
View File
@@ -4,6 +4,7 @@ import { useNavigate } from "react-router-dom";
import { apiRequest } from "../api/client"; import { apiRequest } from "../api/client";
import { useAuth } from "../auth/AuthContext"; import { useAuth } from "../auth/AuthContext";
import type { Me } from "../api/types"; import type { Me } from "../api/types";
import { startseitenPfad } from "../settings/startseite";
const VERANTWORTLICH_ROLLEN = ["administration", "materialverantwortlicher", "leitungsverantwortlicher"]; const VERANTWORTLICH_ROLLEN = ["administration", "materialverantwortlicher", "leitungsverantwortlicher"];
@@ -26,7 +27,7 @@ export function LoginPage() {
// selbst /auth/me abfragen statt auf den Context zu warten. // selbst /auth/me abfragen statt auf den Context zu warten.
const me = await apiRequest<Me>("/auth/me"); const me = await apiRequest<Me>("/auth/me");
const istVerantwortlich = me.rollen.some((r) => VERANTWORTLICH_ROLLEN.includes(r)); const istVerantwortlich = me.rollen.some((r) => VERANTWORTLICH_ROLLEN.includes(r));
navigate(istVerantwortlich ? "/dashboard" : "/objekte"); navigate(startseitenPfad(istVerantwortlich));
} catch { } catch {
setFehler("Login oder Passwort falsch"); setFehler("Login oder Passwort falsch");
} finally { } finally {
+30
View File
@@ -0,0 +1,30 @@
export type StartseitePraeferenz = "auto" | "dashboard" | "objekte";
const SPEICHER_KEY = "mabea-startseite";
export function gespeichertePraeferenz(): StartseitePraeferenz {
try {
const wert = localStorage.getItem(SPEICHER_KEY);
if (wert === "dashboard" || wert === "objekte") return wert;
} catch {
// localStorage evtl. blockiert - "auto" als Fallback
}
return "auto";
}
export function praeferenzSpeichern(praeferenz: StartseitePraeferenz): void {
try {
if (praeferenz === "auto") localStorage.removeItem(SPEICHER_KEY);
else localStorage.setItem(SPEICHER_KEY, praeferenz);
} catch {
// Speichern optional
}
}
/** "auto" = bisheriges Verhalten (Verantwortliche -> Dashboard, sonst Objekte). */
export function startseitenPfad(istVerantwortlich: boolean): string {
const praeferenz = gespeichertePraeferenz();
if (praeferenz === "dashboard" && istVerantwortlich) return "/dashboard";
if (praeferenz === "objekte") return "/objekte";
return istVerantwortlich ? "/dashboard" : "/objekte";
}