import type { AdminLoginInput, AdminUserView } from "@batch-cooking/shared"; import { createContext, type ReactNode, useCallback, useContext, useEffect, useState } from "react"; import { adminApiClient } from "../../api/client"; /** Auth state/actions exposed via {@link useAdminAuth} — the admin-app counterpart of apps/web's `AuthContext`. */ interface AdminAuthContextValue { /** Currently authenticated admin, or `null` if no active session. */ admin: AdminUserView | null; /** True only while the initial `GET /admin/auth/me` check is pending — lets `RequireAdmin` avoid a premature redirect. */ isLoading: boolean; /** Verifies credentials and updates `admin` on success. Throws `ApiError` on failure. */ login: (input: AdminLoginInput) => Promise; /** Ends the session and clears `admin`. */ logout: () => Promise; } const AdminAuthContext = createContext(null); /** * Provides admin authentication state to the whole app. On mount, calls * `GET /admin/auth/me` once to restore the session from the `admin_session` * httpOnly cookie (if any) — same "reload keeps you logged in" behaviour as * apps/web's `AuthProvider`. */ export function AdminAuthProvider({ children }: { children: ReactNode }) { const [admin, setAdmin] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { adminApiClient .me() .then(setAdmin) // No/invalid session — the normal state for a first visit, not an error. .catch(() => setAdmin(null)) .finally(() => setIsLoading(false)); }, []); const login = useCallback(async (input: AdminLoginInput) => { try { setAdmin(await adminApiClient.login(input)); } catch (err) { // Rethrown as-is — `LoginPage`'s submit handler catches and displays // it; this callback just isn't allowed a bare `await`. throw err; } }, []); const logout = useCallback(async () => { try { await adminApiClient.logout(); setAdmin(null); } catch (err) { throw err; // see login()'s catch comment } }, []); return ( {children} ); } /** Reads the current admin auth state/actions. Must be called within an {@link AdminAuthProvider}. */ export function useAdminAuth(): AdminAuthContextValue { const ctx = useContext(AdminAuthContext); if (!ctx) { throw new Error("useAdminAuth must be used within an AdminAuthProvider"); } return ctx; }