tam chat
This commit is contained in:
114
management/src/auth/AuthContext.tsx
Normal file
114
management/src/auth/AuthContext.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user