"use client"; import { createContext, useContext, useEffect, useState, ReactNode } from "react"; import { auth, type Org, type Role, type SessionUser } from "@/lib/api"; export type { Org, Role, SessionUser }; interface AuthContextType { user: SessionUser | null; org: Org | null; /** True for owner and admin — the roles the /api/settings and /api/org routes require. */ isAdmin: boolean; } const AuthContext = createContext({ user: null, org: null, isAdmin: false }); export function useAuth() { return useContext(AuthContext); } /** * Wraps the authenticated app shell only (see app/(app)/layout.tsx). /login and * /setup live outside the group, so no pathname guard is needed here. */ export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); const [org, setOrg] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; (async () => { try { const status = await auth.bootstrapStatus(); if (status.needs_setup) { window.location.href = "/setup"; return; } const me = await auth.me(); if (cancelled) return; setUser(me.user); setOrg(me.org); setLoading(false); } catch (err) { if (cancelled) return; const status = (err as { status?: number }).status; if (status === 401) { window.location.href = "/login"; return; } // Backend unreachable or unexpected failure — don't trap the user on a spinner. setLoading(false); } })(); return () => { cancelled = true; }; }, []); if (loading) { return (
); } const isAdmin = user?.role === "owner" || user?.role === "admin"; return ( {children} ); }