Create Glassmond storefront with Meshkee integration
Build the RTL Next.js experience with live catalog, journal, customer interactions, and SEO-ready canonical routes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { MESHKEE_API_BASE } from "@/lib/meshkee";
|
||||
|
||||
export type AuthUser = {
|
||||
id?: string | number;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
name?: string;
|
||||
cellNumber?: string;
|
||||
email?: string;
|
||||
role?: string;
|
||||
roles?: string[];
|
||||
isAdmin?: boolean;
|
||||
};
|
||||
|
||||
type AuthContextValue = {
|
||||
user: AuthUser | null;
|
||||
token: string | null;
|
||||
loading: boolean;
|
||||
isAdmin: boolean;
|
||||
logout: () => void;
|
||||
};
|
||||
|
||||
const AuthContext = createContext<AuthContextValue>({
|
||||
user: null,
|
||||
token: null,
|
||||
loading: true,
|
||||
isAdmin: false,
|
||||
logout: () => undefined,
|
||||
});
|
||||
|
||||
const tokenKeys = [
|
||||
"accessToken",
|
||||
"meshkeeAccessToken",
|
||||
"meshkee_access_token",
|
||||
"meshkee-token",
|
||||
];
|
||||
|
||||
function readToken() {
|
||||
try {
|
||||
for (const key of tokenKeys) {
|
||||
const value = window.localStorage.getItem(key);
|
||||
if (value) return value;
|
||||
}
|
||||
|
||||
const authValue = window.localStorage.getItem("meshkee-auth");
|
||||
if (authValue) {
|
||||
const auth = JSON.parse(authValue) as { accessToken?: string };
|
||||
if (auth.accessToken) return auth.accessToken;
|
||||
}
|
||||
|
||||
const cookies = document.cookie.split(";").map((item) => item.trim());
|
||||
for (const key of tokenKeys) {
|
||||
const prefix = `${key}=`;
|
||||
const cookie = cookies.find((item) => item.startsWith(prefix));
|
||||
if (cookie) return decodeURIComponent(cookie.slice(prefix.length));
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function clearTokens() {
|
||||
try {
|
||||
tokenKeys.forEach((key) => {
|
||||
window.localStorage.removeItem(key);
|
||||
document.cookie = `${key}=; Max-Age=0; path=/; domain=.glassmond.com`;
|
||||
document.cookie = `${key}=; Max-Age=0; path=/`;
|
||||
});
|
||||
window.localStorage.removeItem("meshkee-auth");
|
||||
} catch {
|
||||
// External customer portal remains the source of truth for authentication.
|
||||
}
|
||||
}
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<AuthUser | null>(null);
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const accessToken = readToken();
|
||||
if (!accessToken) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setToken(accessToken);
|
||||
fetch(`${MESHKEE_API_BASE}/auth/me`, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response.ok) throw new Error("Unauthorized");
|
||||
return response.json() as Promise<{ user: AuthUser }>;
|
||||
})
|
||||
.then((data) => setUser(data.user))
|
||||
.catch(() => {
|
||||
clearTokens();
|
||||
setToken(null);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(() => {
|
||||
clearTokens();
|
||||
setUser(null);
|
||||
setToken(null);
|
||||
}, []);
|
||||
|
||||
const isAdmin = useMemo(() => {
|
||||
const roles = [user?.role, ...(user?.roles ?? [])]
|
||||
.filter(Boolean)
|
||||
.map((role) => String(role).toLowerCase());
|
||||
return Boolean(
|
||||
user?.isAdmin ||
|
||||
roles.some((role) => ["admin", "owner", "superadmin", "business_admin"].includes(role)),
|
||||
);
|
||||
}, [user]);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, token, loading, isAdmin, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
return useContext(AuthContext);
|
||||
}
|
||||
Reference in New Issue
Block a user