diff --git a/DEVLOG.md b/DEVLOG.md index c891b1c..02b6a82 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -6658,3 +6658,17 @@ Keine Commits in dieser Session. - frontend/src/styles/global.css | 2 ++ --- +## 2026-09-07 14:03 – 14:07 (4m) +**Beschreibung:** Claude Code Session +**Projekt:** frontend + +### Commits +- 6db571f fix(dashboard): Resize-Griff war unsichtbar - children-Injection landete in falscher Ebene + +### Geänderte Dateien +- DEVLOG.md | 14 ++++++++++++++ +- frontend/DEVLOG.md | 14 ++++++++++++++ +- frontend/src/pages/dashboard/DashboardPage.tsx | 5 ++--- +- frontend/src/pages/dashboard/TileFrame.tsx | 13 ++++++++++--- + +--- diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 70ac7fc..1a13883 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -21,16 +21,25 @@ function GeschuetzteRoute({ children }: { children: ReactNode }) { } function Startseite() { - const { eingeloggt, istVerantwortlich } = useAuth(); + const { eingeloggt, istVerantwortlich, rollenGeladen } = useAuth(); if (!eingeloggt) return ; + // Ohne dieses Warten würde bei bereits eingeloggter Session (Reload/Direktaufruf) + // kurz mit rollen=[] entschieden und fälschlich auf /objekte redirected, bevor + // /auth/me durch ist. + if (!rollenGeladen) return null; return ; } function AdminRoute({ children }: { children: ReactNode }) { - const { eingeloggt, istVerantwortlich } = useAuth(); + const { eingeloggt, istVerantwortlich, rollenGeladen } = useAuth(); if (!eingeloggt) { return ; } + // Gleiches Race wie in Startseite: bei Direktaufruf/Reload noch nicht auf + // rollen=[] entscheiden, sonst kurzzeitig fälschlicher Redirect. + if (!rollenGeladen) { + return null; + } // Nicht mehr admin-exklusiv: Materialverantwortliche/Leitungsverantwortliche // dürfen rein, sehen dort aber nur die für sie freigeschalteten Tabs // (AdminPage filtert selbst nach Rolle, Backend erzwingt es ohnehin je Endpunkt). diff --git a/frontend/src/auth/AuthContext.tsx b/frontend/src/auth/AuthContext.tsx index 676b592..5424de1 100644 --- a/frontend/src/auth/AuthContext.tsx +++ b/frontend/src/auth/AuthContext.tsx @@ -5,6 +5,12 @@ import type { Me } from "../api/types"; interface AuthContextValue { eingeloggt: boolean; + // Beim Direktladen/Reload einer bereits eingeloggten Session ist "eingeloggt" + // sofort true (Token liegt im Storage), "rollen" aber noch [] bis /auth/me + // durch ist - ohne dieses Flag würden Startseite/AdminRoute in genau diesem + // Fenster fälschlich "istVerantwortlich=false" annehmen und auf /objekte + // redirecten, obwohl die echten Rollen kurz danach eintreffen. + rollenGeladen: boolean; rollen: string[]; istAdmin: boolean; // Rollen-Vorgabe: Materialverantwortliche/Leitungsverantwortliche sahen bisher @@ -23,20 +29,25 @@ const AuthContext = createContext(null); export function AuthProvider({ children }: { children: ReactNode }) { const [eingeloggt, setEingeloggt] = useState(getAuthToken() !== null); const [rollen, setRollen] = useState([]); + const [rollenGeladen, setRollenGeladen] = useState(false); useEffect(() => { if (!eingeloggt) { setRollen([]); + setRollenGeladen(true); return; } + setRollenGeladen(false); apiRequest("/auth/me") .then((me) => setRollen(me.rollen)) - .catch(() => setRollen([])); + .catch(() => setRollen([])) + .finally(() => setRollenGeladen(true)); }, [eingeloggt]); const value = useMemo( () => ({ eingeloggt, + rollenGeladen, rollen, istAdmin: rollen.includes("administration"), istMaterialverantwortlich: rollen.includes("materialverantwortlicher"), @@ -55,7 +66,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { setEingeloggt(false); }, }), - [eingeloggt, rollen] + [eingeloggt, rollenGeladen, rollen] ); return {children};