feat(ui): Dashboard als Startseite für Verantwortliche + einstellbar
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:
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user