chore: update

This commit is contained in:
TakahashiNg
2026-05-13 02:41:32 +00:00
parent bf513bab59
commit 316ff8dacd
4 changed files with 62 additions and 107 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ import { useQuery } from "@apollo/client/react";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
const GET_EATERY_COUNT = gql`{ allEateries { ownerId name } }`;
const GET_EATERY_COUNT = gql`{ allEateries { id } }`;
/**
* Main page — sidebar + product grid layout.
+56 -61
View File
@@ -1,19 +1,27 @@
"use client";
import { eateryClient } from "@/lib/apollo-clients";
import { useAuth } from "@/lib/auth-context";
import { addManagerMenuItem, gqlFetch } from "@/lib/graphql";
import {
MenuItemEntity,
addMenuItemMutation,
allEateriesQuery,
} from "@/lib/types";
import { gql } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client/react";
import { useCallback, useEffect, useState } from "react";
interface MenuItem {
id: string;
name: string;
price: number;
}
const GET_EATERY_MENU = gql`{ allEateries { id menuItems { id name } } }`;
interface Eatery {
id: string;
ownerId: string;
}
const ADD_MENU_ITEM = gql`
mutation addMenuItem($menuItem: AddMenuItemInput!) {
addMenuItem(menuItem: $menuItem) {
id
name
price
}
}
`;
function formatPrice(price: number) {
return price.toLocaleString("vi-VN") + "đ";
@@ -21,8 +29,7 @@ function formatPrice(price: number) {
export default function MenuItemsTab() {
const { user } = useAuth();
const [eateryId, setEateryId] = useState<string | null>(null);
const [menuItems, setMenuItems] = useState<MenuItem[]>([]);
const [menuItems, setMenuItems] = useState<MenuItemEntity[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@@ -32,43 +39,30 @@ export default function MenuItemsTab() {
const [submitting, setSubmitting] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null);
const loadEateryId = useCallback(async (): Promise<string | null> => {
if (!user) return null;
const data = await gqlFetch<{ allEateries: Eatery[] }>(
"{ allEateries { id ownerId } }",
);
return (
data.allEateries.find((e) => e.ownerId === String(user.id))?.id ?? null
);
}, [user]);
const loadMenuItems = useCallback(async (id: string): Promise<MenuItem[]> => {
const data = await gqlFetch<{ menuItemsByEatery: MenuItem[] }>(
`query($eateryId: String!) {
menuItemsByEatery(eateryId: $eateryId) { id name price }
}`,
{ eateryId: id },
);
return data.menuItemsByEatery ?? [];
}, []);
const {
data,
loading: gqlLoading,
error: gqlError,
} = useQuery<allEateriesQuery>(GET_EATERY_MENU, {
client: eateryClient,
fetchPolicy: "no-cache",
});
useEffect(() => {
async function init() {
setLoading(true);
setError(null);
try {
const id = await loadEateryId();
setEateryId(id);
if (id) setMenuItems(await loadMenuItems(id));
else setMenuItems([]);
} catch {
setError("Không thể tải dữ liệu menu. Kiểm tra kết nối backend.");
} finally {
setLoading(false);
}
setLoading(true);
setError(null);
if (!gqlLoading) {
if (data) setMenuItems(data.allEateries[0].menuItems);
else
setError(
gqlError?.message ||
"Không thể tải dữ liệu menu. Kiểm tra kết nối backend.",
);
}
init();
}, [loadEateryId, loadMenuItems]);
setLoading(false);
}, [data, gqlLoading, gqlError]);
const handleAdd = async (e: React.FormEvent) => {
e.preventDefault();
@@ -76,11 +70,23 @@ export default function MenuItemsTab() {
setSubmitting(true);
setSubmitError(null);
try {
const added = await addManagerMenuItem(
newName.trim(),
parseFloat(newPrice),
const [addMenuItem, { data }] = useMutation<addMenuItemMutation>(
GET_EATERY_MENU,
{
client: eateryClient,
fetchPolicy: "no-cache",
},
);
if (added) setMenuItems((prev) => [...prev, added]);
await addMenuItem({
variables: {
name: newName,
price: newPrice,
},
});
if (data) setMenuItems((prev) => [...prev, data.addMenuItem]);
setNewName("");
setNewPrice("");
setShowForm(false);
@@ -116,17 +122,6 @@ export default function MenuItemsTab() {
);
}
if (!eateryId) {
return (
<div className="flex flex-col items-center justify-center py-16 text-(--color-text-muted)">
<i className="fa-solid fa-store mb-2 block text-3xl opacity-30"></i>
<p className="text-sm">
Quán của bạn chưa đưc khởi tạo trong hệ thống.
</p>
</div>
);
}
return (
<div className="space-y-4">
{/* Toolbar */}
-45
View File
@@ -1,45 +0,0 @@
import type { Eatery } from "./types";
export async function gqlFetch<T = Record<string, unknown>>(
query: string,
variables?: Record<string, unknown>,
): Promise<T> {
const res = await fetch("/api/eatery/graphql", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ query, variables }),
});
if (!res.ok) throw new Error(`GraphQL request failed: ${res.status}`);
return res.json() as Promise<T>;
}
export async function addManagerMenuItem(
name: string,
price: number,
): Promise<{ id: string; name: string; price: number } | null> {
const data = await gqlFetch<{
addMenuItem: { id: string; name: string; price: number } | null;
}>(
`mutation($name: String!, $price: Float!) {
addMenuItem(name: $name, price: $price) { id name price }
}`,
{ name, price },
);
return data.addMenuItem;
}
export async function fetchManagerEatery(): Promise<Eatery | null> {
const data = await gqlFetch<{ eateriesByOwner: Eatery | null }>(`
query {
eateriesByOwner {
id
ownerId
name
isVerified
}
}
`);
return data.eateriesByOwner;
}
+5
View File
@@ -147,6 +147,7 @@ export interface Department {
}
export interface MenuItemEntity {
id: string;
name: string;
price: number;
eatery: EateryEntity;
@@ -165,3 +166,7 @@ export interface EateryEntity {
export interface allEateriesQuery {
allEateries: EateryEntity[];
}
export interface addMenuItemMutation {
addMenuItem: MenuItemEntity;
}