Enhance documentation, implement Twilio OTP delivery, and update payment gateway methods. Updated AGENTS.md and README.md for clarity on ExecPlans and architecture. Added Twilio as a dependency and implemented capture/refund methods in MoyasarGateway. Improved frontend routing with react-router-dom and added authentication context. Updated styles and localization files for better user experience.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
||||
import { apiGet, apiPost } from "../api/client";
|
||||
|
||||
const STORAGE_ACCESS = "auth_access";
|
||||
const STORAGE_REFRESH = "auth_refresh";
|
||||
|
||||
const AuthContext = createContext(null);
|
||||
|
||||
export function AuthProvider({ children }) {
|
||||
const [user, setUser] = useState(null);
|
||||
const [accessToken, setAccessToken] = useState(() => {
|
||||
if (typeof window === "undefined") return null;
|
||||
return localStorage.getItem(STORAGE_ACCESS);
|
||||
});
|
||||
const [refreshToken, setRefreshToken] = useState(() => {
|
||||
if (typeof window === "undefined") return null;
|
||||
return localStorage.getItem(STORAGE_REFRESH);
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const persistTokens = useCallback((access, refresh) => {
|
||||
setAccessToken(access);
|
||||
setRefreshToken(refresh);
|
||||
if (typeof window !== "undefined") {
|
||||
if (access) localStorage.setItem(STORAGE_ACCESS, access);
|
||||
else localStorage.removeItem(STORAGE_ACCESS);
|
||||
if (refresh) localStorage.setItem(STORAGE_REFRESH, refresh);
|
||||
else localStorage.removeItem(STORAGE_REFRESH);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(() => {
|
||||
setUser(null);
|
||||
persistTokens(null, null);
|
||||
}, [persistTokens]);
|
||||
|
||||
const login = useCallback((access, refresh, userData) => {
|
||||
persistTokens(access, refresh);
|
||||
setUser(userData);
|
||||
}, [persistTokens]);
|
||||
|
||||
// Restore user from token on mount
|
||||
useEffect(() => {
|
||||
if (!accessToken) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
apiGet("/auth/me/", accessToken)
|
||||
.then((data) => {
|
||||
setUser(data);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
// Token invalid, try refresh
|
||||
if (!refreshToken) {
|
||||
logout();
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
apiPost("/auth/token/refresh/", { refresh: refreshToken })
|
||||
.then(({ access }) => {
|
||||
persistTokens(access, refreshToken);
|
||||
return apiGet("/auth/me/", access);
|
||||
})
|
||||
.then((data) => {
|
||||
setUser(data);
|
||||
})
|
||||
.catch(() => {
|
||||
logout();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
});
|
||||
}, [accessToken, refreshToken, logout, persistTokens]);
|
||||
|
||||
const value = {
|
||||
user,
|
||||
accessToken,
|
||||
loading,
|
||||
login,
|
||||
logout,
|
||||
isAuthenticated: !!user,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user