chore: updat

This commit is contained in:
TakahashiNg
2026-05-13 03:56:42 +00:00
parent 02a09a51d1
commit 65b9387105
4 changed files with 56 additions and 63 deletions
+19 -40
View File
@@ -1,6 +1,6 @@
"use client";
import type { Product } from "@/lib/types";
import type { MenuItemEntity } from "@/lib/types";
import { useState } from "react";
import type { ProductModalProps } from "./Manager.types";
@@ -12,11 +12,10 @@ export default function ProductModal({
onClose,
}: ProductModalProps) {
const isEdit = product !== null;
const [form, setForm] = useState<Omit<Product, "id">>({
const [form, setForm] = useState<Omit<MenuItemEntity, "id">>({
name: product?.name ?? "",
category: product?.category ?? categories[0]?.id ?? "",
price: product?.price ?? 0,
image: product?.image ?? "/imgs/products/placeholder.jpg",
imageUrl: product?.image ?? "/imgs/products/placeholder.jpg",
description: product?.description ?? "",
available: product?.available ?? true,
});
@@ -67,42 +66,22 @@ export default function ProductModal({
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
Danh mục <span className="text-red-500">*</span>
</label>
<select
required
title="Chọn danh mục"
value={form.category}
onChange={(e) => setForm({ ...form, category: e.target.value })}
className={inputCls}
>
{categories.map((cat) => (
<option key={cat.id} value={cat.id}>
{cat.name}
</option>
))}
</select>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
Giá (đ) <span className="text-red-500">*</span>
</label>
<input
required
type="number"
min={0}
step={1000}
value={form.price}
onChange={(e) =>
setForm({ ...form, price: Number(e.target.value) })
}
className={inputCls}
placeholder="25000"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
Giá (đ) <span className="text-red-500">*</span>
</label>
<input
required
type="number"
min={0}
step={1000}
value={form.price}
onChange={(e) =>
setForm({ ...form, price: Number(e.target.value) })
}
className={inputCls}
placeholder="25000"
/>
</div>
<div>
+7 -19
View File
@@ -1,7 +1,7 @@
"use client";
import { useManager } from "@/lib/manager-context";
import type { Product } from "@/lib/types";
import type { MenuItemEntity } from "@/lib/types";
import { useState } from "react";
import DeleteConfirm from "./DeleteConfirm";
@@ -27,13 +27,12 @@ export default function ProductsTab() {
"all" | "available" | "unavailable"
>("all");
const [search, setSearch] = useState("");
const [modalProduct, setModalProduct] = useState<Product | null | "new">(
null,
);
const [deleteTarget, setDeleteTarget] = useState<Product | null>(null);
const [modalProduct, setModalProduct] = useState<
MenuItemEntity | null | "new"
>(null);
const [deleteTarget, setDeleteTarget] = useState<MenuItemEntity | null>(null);
const filtered = products.filter((p) => {
if (filterCategory !== "all" && p.category !== filterCategory) return false;
if (filterStatus === "available" && p.available === false) return false;
if (filterStatus === "unavailable" && p.available !== false) return false;
if (
@@ -125,9 +124,6 @@ export default function ProductsTab() {
<th className="px-4 py-3 text-left font-semibold text-(--color-text-secondary)">
Tên món
</th>
<th className="px-4 py-3 text-left font-semibold text-(--color-text-secondary)">
Danh mục
</th>
<th className="px-4 py-3 text-right font-semibold text-(--color-text-secondary)">
Giá
</th>
@@ -166,14 +162,6 @@ export default function ProductsTab() {
)}
</div>
</td>
<td className="px-4 py-3">
<span className="inline-flex items-center gap-1.5 rounded-full bg-(--color-accent-light) px-2.5 py-0.5 text-xs font-medium text-(--color-primary-dark)">
<i
className={`${categories.find((c) => c.id === p.category)?.icon ?? "fa-solid fa-tag"} text-[10px]`}
></i>
{getCategoryName(p.category)}
</span>
</td>
<td className="px-4 py-3 text-right font-semibold text-(--color-primary)">
{formatPrice(p.price)}
</td>
@@ -215,9 +203,9 @@ export default function ProductsTab() {
<ProductModal
product={modalProduct === "new" ? null : modalProduct}
categories={categories}
onSave={(data) => {
onSave={(data: MenuItemEntity) => {
if ("id" in data) {
updateProduct(data as Product);
updateProduct(data);
} else {
addProduct(data);
}
+28 -3
View File
@@ -1,7 +1,7 @@
"use client";
import { gql } from "@apollo/client";
import { useQuery } from "@apollo/client/react";
import { useMutation, useQuery } from "@apollo/client/react";
import {
ReactNode,
createContext,
@@ -16,6 +16,7 @@ import type {
Combo,
MenuCategory,
MenuItemEntity,
addMenuItemMutation,
allEateriesQuery,
} from "./types";
@@ -64,12 +65,26 @@ const GET_EATERY_MENU = gql`
menuItems {
id
name
available
description
price
}
}
}
`;
const ADD_MENU_ITEM = gql`
mutation addMenuItem($menuItem: AddMenuItemInput!) {
addMenuItem(menuItem: $menuItem) {
id
name
available
description
price
}
}
`;
// ─── Provider ─────────────────────────────────────────────────────────────────
export function ManagerProvider({ children }: { children: ReactNode }) {
@@ -86,6 +101,10 @@ export function ManagerProvider({ children }: { children: ReactNode }) {
fetchPolicy: "network-only",
});
const [mutateAddMenuItem] = useMutation<addMenuItemMutation>(ADD_MENU_ITEM, {
client: eateryClient,
});
useEffect(() => {
if (data?.allEateries?.[0]) {
setProducts(data.allEateries[0].menuItems);
@@ -94,8 +113,14 @@ export function ManagerProvider({ children }: { children: ReactNode }) {
// ── MenuItemEntity actions ──────────────────────────────────────────────────────
const addProduct = (product: MenuItemEntity) => {
setProducts((prev) => [...prev, product]);
const addProduct = async (product: MenuItemEntity) => {
const { data } = await mutateAddMenuItem({
variables: {
menuItem: product,
},
});
if (data) setProducts((prev) => [...prev, data.addMenuItem]);
};
const updateProduct = (product: MenuItemEntity) => {
+2 -1
View File
@@ -139,9 +139,10 @@ export interface MenuItemEntity {
id: string;
name: string;
price: number;
eatery: EateryEntity;
eatery?: EateryEntity;
imageUrl: string;
available: boolean;
description: string;
}
export interface ShiftEntity {}