3a5cfd0494
Release package / release (push) Successful in 12m29s
Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com> Reviewed-on: #45
226 lines
6.1 KiB
TypeScript
226 lines
6.1 KiB
TypeScript
"use client";
|
|
|
|
import { gql } from "@apollo/client";
|
|
import { useMutation, useQuery } from "@apollo/client/react";
|
|
import {
|
|
ReactNode,
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
useState,
|
|
} from "react";
|
|
|
|
import { eateryClient } from "./apollo-clients";
|
|
import {
|
|
EateryEntity,
|
|
type MenuItemEntity,
|
|
type addMenuItemMutation,
|
|
type allEateriesQuery,
|
|
deleteMenuItemMutation,
|
|
updateMenuItemMutation,
|
|
} from "./types";
|
|
|
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
|
|
export type ManagerTab = "products" | "reviews";
|
|
|
|
interface ManagerContextType {
|
|
// Data
|
|
products: MenuItemEntity[];
|
|
eateryId: string | null;
|
|
eatery?: EateryEntity;
|
|
|
|
// Active tab
|
|
activeTab: ManagerTab;
|
|
setActiveTab: (tab: ManagerTab) => void;
|
|
|
|
// MenuItemEntity actions
|
|
addProduct: (product: MenuItemEntity) => void;
|
|
updateProduct: (product: MenuItemEntity) => void;
|
|
deleteProduct: (id: string) => void;
|
|
toggleProductAvailability: (product: MenuItemEntity) => void;
|
|
}
|
|
|
|
// ─── Context ──────────────────────────────────────────────────────────────────
|
|
|
|
const ManagerContext = createContext<ManagerContextType | undefined>(undefined);
|
|
|
|
// ___ Graphql __________________________________________________________________
|
|
|
|
const GET_EATERY_MENU = gql`
|
|
query GetEateryMenu {
|
|
allEateries {
|
|
id
|
|
name
|
|
menuItems {
|
|
id
|
|
name
|
|
imageUrl
|
|
available
|
|
description
|
|
price
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const ADD_MENU_ITEM = gql`
|
|
mutation addMenuItem($menuItem: AddMenuItemInput!) {
|
|
addMenuItem(menuItem: $menuItem) {
|
|
id
|
|
name
|
|
imageUrl
|
|
available
|
|
description
|
|
price
|
|
}
|
|
}
|
|
`;
|
|
|
|
const UPDATE_MENU_ITEM = gql`
|
|
mutation updateMenuItem($menuItem: UpdateMenuItemInput!) {
|
|
updateMenuItem(menuItem: $menuItem) {
|
|
id
|
|
name
|
|
imageUrl
|
|
available
|
|
description
|
|
price
|
|
}
|
|
}
|
|
`;
|
|
|
|
const DELETE_MENU_ITEM = gql`
|
|
mutation deleteMenuItem($input: String!) {
|
|
deleteMenuItem(menuItemId: $input)
|
|
}
|
|
`;
|
|
|
|
// ─── Provider ─────────────────────────────────────────────────────────────────
|
|
|
|
export function ManagerProvider({ children }: { children: ReactNode }) {
|
|
const [products, setProducts] = useState<MenuItemEntity[]>([]);
|
|
const [eatery, setEatery] = useState<EateryEntity>();
|
|
const [activeTab, setActiveTab] = useState<ManagerTab>("products");
|
|
|
|
const { data } = useQuery<allEateriesQuery>(GET_EATERY_MENU, {
|
|
client: eateryClient,
|
|
fetchPolicy: "network-only",
|
|
});
|
|
|
|
const eateryId = data?.allEateries?.[0]?.id ?? null;
|
|
|
|
const [mutateAddMenuItem] = useMutation<addMenuItemMutation>(ADD_MENU_ITEM, {
|
|
client: eateryClient,
|
|
});
|
|
|
|
const [mutateUpdateMenuItem] = useMutation<updateMenuItemMutation>(
|
|
UPDATE_MENU_ITEM,
|
|
{ client: eateryClient },
|
|
);
|
|
|
|
const [mutateDeleteMenuItem] = useMutation<deleteMenuItemMutation>(
|
|
DELETE_MENU_ITEM,
|
|
{ client: eateryClient },
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (data?.allEateries?.[0]) {
|
|
setProducts(data.allEateries[0].menuItems);
|
|
setEatery(data.allEateries[0]);
|
|
}
|
|
}, [data]);
|
|
|
|
// ── MenuItemEntity actions ──────────────────────────────────────────────────────
|
|
|
|
const addProduct = async (product: MenuItemEntity) => {
|
|
const { data } = await mutateAddMenuItem({
|
|
variables: {
|
|
menuItem: product,
|
|
},
|
|
});
|
|
|
|
if (!data) return;
|
|
|
|
// addMenuItem backend does not persist imageUrl (constructor only maps name+price).
|
|
// If the caller provided an imageUrl, patch it immediately via updateMenuItem
|
|
// which uses MapStruct and correctly saves all fields.
|
|
if (product.imageUrl && data.addMenuItem?.id) {
|
|
const { data: updated } = await mutateUpdateMenuItem({
|
|
variables: {
|
|
menuItem: { id: data.addMenuItem.id, imageUrl: product.imageUrl },
|
|
},
|
|
});
|
|
if (updated) {
|
|
setProducts((prev) => [...prev, updated.updateMenuItem]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
setProducts((prev) => [...prev, data.addMenuItem]);
|
|
};
|
|
|
|
const updateProduct = async (product: MenuItemEntity) => {
|
|
const { data } = await mutateUpdateMenuItem({
|
|
variables: {
|
|
menuItem: product,
|
|
},
|
|
});
|
|
|
|
if (data)
|
|
setProducts((prev) =>
|
|
prev.map((p) => (p.id === product.id ? data.updateMenuItem : p)),
|
|
);
|
|
};
|
|
|
|
const deleteProduct = async (id: string) => {
|
|
const { data } = await mutateDeleteMenuItem({ variables: { input: id } });
|
|
|
|
if (data)
|
|
setProducts((prev) =>
|
|
prev.filter((p) => !(p.id == id && data.deleteMenuItem)),
|
|
);
|
|
};
|
|
|
|
const toggleProductAvailability = async (product: MenuItemEntity) => {
|
|
const { data } = await mutateUpdateMenuItem({
|
|
variables: {
|
|
menuItem: { ...product, available: !product.available },
|
|
},
|
|
});
|
|
|
|
if (data)
|
|
setProducts((prev) =>
|
|
prev.map((p) => (p.id === product.id ? data.updateMenuItem : p)),
|
|
);
|
|
};
|
|
|
|
return (
|
|
<ManagerContext.Provider
|
|
value={{
|
|
eatery,
|
|
products,
|
|
eateryId,
|
|
activeTab,
|
|
setActiveTab,
|
|
addProduct,
|
|
updateProduct,
|
|
deleteProduct,
|
|
toggleProductAvailability,
|
|
}}
|
|
>
|
|
{children}
|
|
</ManagerContext.Provider>
|
|
);
|
|
}
|
|
|
|
// ─── Hook ─────────────────────────────────────────────────────────────────────
|
|
|
|
export function useManager() {
|
|
const context = useContext(ManagerContext);
|
|
if (context === undefined) {
|
|
throw new Error("useManager must be used within a ManagerProvider");
|
|
}
|
|
return context;
|
|
}
|