trptk/components/auth/AuthContext.tsx
2026-02-24 17:14:07 +01:00

132 lines
3.2 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from "react";
export type Customer = {
id: string;
email: string;
first_name: string | null;
last_name: string | null;
phone: string | null;
created_at: string;
};
type AuthState = {
customer: Customer | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
register: (data: {
email: string;
password: string;
first_name: string;
last_name: string;
}) => Promise<void>;
logout: () => Promise<void>;
refreshCustomer: () => Promise<void>;
};
const AuthContext = createContext<AuthState | null>(null);
async function apiJson<T>(url: string, init?: RequestInit): Promise<T> {
const res = await fetch(url, {
...init,
headers: { "Content-Type": "application/json", ...init?.headers },
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
const msg = body?.error ?? `Auth API error: ${res.status}`;
throw new Error(msg);
}
return res.json();
}
export function AuthProvider({ children }: { children: ReactNode }) {
const [customer, setCustomer] = useState<Customer | null>(null);
const [isLoading, setIsLoading] = useState(true);
// On mount: check if user is already authenticated via cookie
useEffect(() => {
async function checkAuth() {
try {
const data = await apiJson<{ customer: Customer }>("/api/account/me");
setCustomer(data.customer);
} catch {
setCustomer(null);
} finally {
setIsLoading(false);
}
}
checkAuth();
}, []);
const login = useCallback(async (email: string, password: string) => {
const data = await apiJson<{ customer: Customer }>("/api/account/login", {
method: "POST",
body: JSON.stringify({ email, password }),
});
setCustomer(data.customer);
}, []);
const register = useCallback(
async (regData: {
email: string;
password: string;
first_name: string;
last_name: string;
}) => {
const data = await apiJson<{ customer: Customer }>(
"/api/account/register",
{
method: "POST",
body: JSON.stringify(regData),
},
);
setCustomer(data.customer);
},
[],
);
const logout = useCallback(async () => {
await apiJson("/api/account/logout", { method: "POST" });
setCustomer(null);
}, []);
const refreshCustomer = useCallback(async () => {
try {
const data = await apiJson<{ customer: Customer }>("/api/account/me");
setCustomer(data.customer);
} catch {
setCustomer(null);
}
}, []);
const value = useMemo<AuthState>(
() => ({
customer,
isAuthenticated: !!customer,
isLoading,
login,
register,
logout,
refreshCustomer,
}),
[customer, isLoading, login, register, logout, refreshCustomer],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within AuthProvider");
return ctx;
}