Replace entire repo content with code from /root/shahikitchen-google/

This commit is contained in:
root
2026-07-03 02:51:15 +00:00
parent c8b7dc95e4
commit 8ccf033329
79 changed files with 7611 additions and 439 deletions
@@ -0,0 +1,123 @@
'use client';
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from 'react';
interface CustomerAuthContextValue {
email: string | null;
name: string | null;
isAuthenticated: boolean;
isMenuManager: boolean;
isLoading: boolean;
refreshSession: () => Promise<void>;
logout: () => Promise<void>;
}
const CustomerAuthContext = createContext<CustomerAuthContextValue>({
email: null,
name: null,
isAuthenticated: false,
isMenuManager: false,
isLoading: true,
refreshSession: async () => {},
logout: async () => {},
});
export function CustomerAuthProvider({ children }: { children: ReactNode }) {
const [email, setEmail] = useState<string | null>(null);
const [name, setName] = useState<string | null>(null);
const [isMenuManager, setIsMenuManager] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const refreshSession = useCallback(async () => {
try {
const response = await fetch('/api/auth/customer/session', { cache: 'no-store' });
const data = (await response.json()) as {
authenticated: boolean;
email: string | null;
name?: string | null;
isMenuManager?: boolean;
};
if (data.authenticated && data.email) {
setEmail(data.email);
setName(data.name ?? null);
setIsMenuManager(Boolean(data.isMenuManager));
} else {
setEmail(null);
setName(null);
setIsMenuManager(false);
}
} catch {
setEmail(null);
setName(null);
setIsMenuManager(false);
} finally {
setIsLoading(false);
}
}, []);
const logout = useCallback(async () => {
try {
await fetch('/api/auth/customer/logout', {
method: 'POST',
credentials: 'include',
cache: 'no-store',
});
} finally {
setEmail(null);
setName(null);
setIsMenuManager(false);
window.dispatchEvent(new Event('shahi-customer-auth-changed'));
window.location.assign('/');
}
}, []);
useEffect(() => {
void refreshSession();
}, [refreshSession]);
useEffect(() => {
const onAuthChanged = () => {
void refreshSession();
};
const onVisible = () => {
if (document.visibilityState === 'visible') {
void refreshSession();
}
};
window.addEventListener('shahi-customer-auth-changed', onAuthChanged);
document.addEventListener('visibilitychange', onVisible);
return () => {
window.removeEventListener('shahi-customer-auth-changed', onAuthChanged);
document.removeEventListener('visibilitychange', onVisible);
};
}, [refreshSession]);
const value = useMemo(
() => ({
email,
name,
isAuthenticated: Boolean(email),
isMenuManager,
isLoading,
refreshSession,
logout,
}),
[email, name, isMenuManager, isLoading, refreshSession, logout],
);
return <CustomerAuthContext.Provider value={value}>{children}</CustomerAuthContext.Provider>;
}
export function useCustomerAuth() {
return useContext(CustomerAuthContext);
}
+99
View File
@@ -0,0 +1,99 @@
'use client';
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from 'react';
import type { MenuCategory, MenuItem } from '@/domain/menu/entities';
import {
allMenuItems as staticAllMenuItems,
menuCategories as staticMenuCategories,
} from '@/infrastructure/menu/static-menu-data';
interface MenuContextValue {
categories: MenuCategory[];
allItems: MenuItem[];
getItemById: (id: string) => MenuItem | undefined;
isLoading: boolean;
refreshMenu: () => Promise<void>;
}
const MenuContext = createContext<MenuContextValue>({
categories: staticMenuCategories,
allItems: staticAllMenuItems,
getItemById: (id) => staticAllMenuItems.find((item) => item.id === id),
isLoading: false,
refreshMenu: async () => {},
});
export function MenuProvider({ children }: { children: ReactNode }) {
const [categories, setCategories] = useState<MenuCategory[]>(staticMenuCategories);
const [isLoading, setIsLoading] = useState(true);
const refreshMenu = useCallback(async () => {
try {
const response = await fetch('/api/menu', { cache: 'no-store' });
if (!response.ok) return;
const data = (await response.json()) as { categories?: MenuCategory[] };
if (Array.isArray(data.categories) && data.categories.length > 0) {
setCategories(data.categories);
}
} catch {
// Keep the last known menu (static seed on first load).
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
void refreshMenu();
}, [refreshMenu]);
// Refetch when the tab becomes visible again (e.g. after editing in /admin).
useEffect(() => {
const onVisible = () => {
if (document.visibilityState === 'visible') {
void refreshMenu();
}
};
const onMenuUpdated = () => {
void refreshMenu();
};
document.addEventListener('visibilitychange', onVisible);
window.addEventListener('shahi-menu-updated', onMenuUpdated);
return () => {
document.removeEventListener('visibilitychange', onVisible);
window.removeEventListener('shahi-menu-updated', onMenuUpdated);
};
}, [refreshMenu]);
const allItems = useMemo(() => categories.flatMap((category) => category.items), [categories]);
const getItemById = useCallback(
(id: string) => allItems.find((item) => item.id === id),
[allItems],
);
const value = useMemo(
() => ({
categories,
allItems,
getItemById,
isLoading,
refreshMenu,
}),
[categories, allItems, getItemById, isLoading, refreshMenu],
);
return <MenuContext.Provider value={value}>{children}</MenuContext.Provider>;
}
export function useMenu() {
return useContext(MenuContext);
}