Files
rikkei_simple_care/management/src/auth/AuthContext.tsx
2026-06-30 10:39:29 +07:00

115 lines
3.3 KiB
TypeScript

import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
import { apiAuth } from '../api';
export interface StaffUser {
id: number;
email: string;
fullName: string;
mustChangePassword: boolean;
}
interface AuthState {
token: string | null;
staff: StaffUser | null;
loading: boolean;
login: (email: string, password: string) => Promise<void>;
provision: (email: string) => Promise<string>;
logout: () => void;
changePassword: (oldPassword: string, newPassword: string) => Promise<void>;
forgotPassword: (email: string) => Promise<string>;
resetPassword: (token: string, newPassword: string) => Promise<void>;
refreshMe: () => Promise<void>;
}
const TOKEN_KEY = 'sc_staff_token';
const AuthContext = createContext<AuthState | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [token, setToken] = useState<string | null>(() => localStorage.getItem(TOKEN_KEY));
const [staff, setStaff] = useState<StaffUser | null>(null);
const [loading, setLoading] = useState(true);
const applySession = useCallback((nextToken: string | null, nextStaff: StaffUser | null) => {
setToken(nextToken);
setStaff(nextStaff);
if (nextToken) {
localStorage.setItem(TOKEN_KEY, nextToken);
} else {
localStorage.removeItem(TOKEN_KEY);
}
}, []);
const refreshMe = useCallback(async () => {
if (!token) {
setStaff(null);
setLoading(false);
return;
}
try {
const res = await apiAuth.me();
setStaff(res.staff);
} catch {
applySession(null, null);
} finally {
setLoading(false);
}
}, [token, applySession]);
useEffect(() => {
refreshMe();
}, [refreshMe]);
const login = useCallback(async (email: string, password: string) => {
const res = await apiAuth.login(email, password);
applySession(res.token, res.staff);
}, [applySession]);
const provision = useCallback(async (email: string) => {
const res = await apiAuth.provision(email);
return res.message;
}, []);
const logout = useCallback(() => {
applySession(null, null);
}, [applySession]);
const changePassword = useCallback(async (oldPassword: string, newPassword: string) => {
const res = await apiAuth.changePassword(oldPassword, newPassword);
applySession(res.token, res.staff);
}, [applySession]);
const forgotPassword = useCallback(async (email: string) => {
const res = await apiAuth.forgotPassword(email);
return res.message;
}, []);
const resetPassword = useCallback(async (resetToken: string, newPassword: string) => {
await apiAuth.resetPassword(resetToken, newPassword);
}, []);
const value = useMemo<AuthState>(() => ({
token,
staff,
loading,
login,
provision,
logout,
changePassword,
forgotPassword,
resetPassword,
refreshMe,
}), [token, staff, loading, login, provision, logout, changePassword, forgotPassword, resetPassword, refreshMe]);
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;
}
export function getStoredToken(): string | null {
return localStorage.getItem(TOKEN_KEY);
}