{
if (e.key === "Enter" || e.key === " ") {
@@ -49,13 +52,23 @@ function MenuCard({
tabIndex={0}
onClick={onClick}
>
-

+
{!disabled && (
e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
+ aria-label="메뉴 선택 체크박스"
>
) {
+ const { storeId } = useStoreId();
+ const { mutate: deleteMenu } = useMutation(menuMutations.deleteMenu());
+ const { mutate: multiDeleteMenus } = useMutation(menuMutations.multiDeleteMenus());
+
const renderText = () => {
if (deleteItems?.length === 1) {
return `을`;
@@ -15,7 +26,46 @@ function MenuDeleteAlert({ deleteItems, ...props }: Readonly {
- // TODO: 메뉴 삭제 로직 구현
+ if (!deleteItems?.length) return;
+
+ if (deleteItems?.length === 1) {
+ deleteMenu(
+ {
+ storeId: storeId!,
+ menuId: deleteItems[0].menuId,
+ categoryId: deleteItems[0].categoryId,
+ },
+ {
+ onSuccess: () => {
+ toast.success("메뉴 삭제가 완료되었습니다.");
+ queryClient.invalidateQueries({ queryKey: MENUS_KEY.menu });
+ props.close();
+ },
+ onError: (error) => {
+ toast.error(errorResponse(error).data.message);
+ },
+ }
+ );
+ } else {
+ multiDeleteMenus(
+ {
+ storeId: storeId!,
+ data: {
+ menuIds: deleteItems.map((item) => item.menuId),
+ },
+ },
+ {
+ onSuccess: () => {
+ toast.success("메뉴 삭제가 완료되었습니다.");
+ queryClient.invalidateQueries({ queryKey: MENUS_KEY.menu });
+ props.close();
+ },
+ onError: (error) => {
+ toast.error(errorResponse(error).data.message);
+ },
+ }
+ );
+ }
};
return (
diff --git a/src/pages/main/owner/menus/MenuDetailContent.tsx b/src/pages/main/owner/menus/MenuDetailContent.tsx
index ee15516..35e85d5 100644
--- a/src/pages/main/owner/menus/MenuDetailContent.tsx
+++ b/src/pages/main/owner/menus/MenuDetailContent.tsx
@@ -1,12 +1,14 @@
-import { useState } from "react";
+import { useEffect, useRef, useState, type ChangeEvent } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import logo from "@/assets/images/logo.svg";
+import Spinner from "@/components/feedback/Spinner";
import { Form, FormErrorMessage } from "@/components/form/Form";
import { Close } from "@/components/icons";
import { Dialog } from "@/components/overlay/Dialog";
import Button from "@/components/ui/Button/Button";
import Image from "@/components/ui/Image";
+import useMenuFormSubmit from "@/hooks/menu/useMenuFormSubmit";
import cn from "@/lib/utils";
import MenuDetailForm from "@/pages/main/owner/menus/MenuDetailForm";
import MenuDetailOptions from "@/pages/main/owner/menus/MenuDetailOptions";
@@ -18,8 +20,7 @@ type MenuDetailMode = "create" | "detail" | "edit";
interface MenuDetailContentProps extends ModalProps {
entry: "create" | "detail";
- menu: MenuDetail;
- close: () => void;
+ menu?: MenuDetail;
initialCategoryId?: string;
}
@@ -29,85 +30,87 @@ function MenuDetailContent({
close,
initialCategoryId,
}: Readonly) {
+ const imageRef = useRef(null);
+
const [mode, setMode] = useState(entry === "create" ? "create" : "detail");
+ const [imageFile, setImageFile] = useState(null);
+ const [selectedGroup, setSelectedGroup] = useState("MANDATORY");
- const isEditing = mode === "edit";
const isCreating = mode === "create";
- const isDetail = mode === "detail";
-
- const canEdit = isCreating || isEditing;
const form = useForm({
resolver: zodResolver(menuSchema),
mode: "onSubmit",
reValidateMode: "onChange",
- defaultValues: isCreating
- ? {
- categoryId: initialCategoryId,
- name: "",
- description: "",
- price: "",
- spicy: 0,
- state: "DEFAULT",
- label: "DEFAULT",
- image: "",
- printEnabled: true,
- requiredOptionGroups: [],
- optionalOptionGroups: [],
- }
- : {
- categoryId: menu?.categoryId ?? "",
- name: menu?.name ?? "",
- description: menu?.description,
- price: menu?.price ? menu.price.toLocaleString("ko-KR") : "",
- spicy: menu?.spicy ?? 0,
- state: menu?.state ?? "DEFAULT",
- label: menu?.label ?? "DEFAULT",
- image: menu?.image ?? "",
- printEnabled: menu?.printEnabled ?? true,
- requiredOptionGroups:
- menu?.menuOptionGroups
- .filter((group) => group.type === "MANDATORY")
- .map((group) => ({
- name: group.name,
- type: group.type,
- printEnabled: group.printEnabled,
- menuOptions: group.menuOptions.map((option) => ({
- name: option.name,
- price: option.price.toLocaleString("ko-KR"),
- })),
- })) ?? [],
- optionalOptionGroups:
- menu?.menuOptionGroups
- .filter((group) => group.type === "OPTIONAL")
- .map((group) => ({
- name: group.name,
- type: group.type,
- printEnabled: group.printEnabled,
- menuOptions: group.menuOptions.map((option) => ({
- name: option.name,
- price: option.price.toLocaleString("ko-KR"),
- })),
- })) ?? [],
- },
+ defaultValues: {
+ categoryId: initialCategoryId ?? "",
+ name: "",
+ description: "",
+ price: "",
+ spicy: 0,
+ state: "DEFAULT",
+ label: "DEFAULT",
+ image: "",
+ printEnabled: true,
+ requiredOptionGroups: [],
+ optionalOptionGroups: [],
+ },
});
- const [selectedGroup, setSelectedGroup] = useState("MANDATORY");
-
- const handleSubmit = form.handleSubmit(() => {
- if (isCreating) {
- // TODO: 메뉴 생성 로직 추가
- // TODO: 메뉴의 가격과 옵션의 가격을 number로 변경
- } else {
- // TODO: 메뉴 수정 로직 추가
- setMode("detail");
+ useEffect(() => {
+ if (menu?.menuId) {
+ form.reset({
+ ...menu,
+ price: menu?.price ? menu.price.toLocaleString("ko-KR") : "",
+ requiredOptionGroups:
+ menu?.menuOptionGroups
+ .filter((group) => group.type === "MANDATORY")
+ .map((group) => ({
+ ...group,
+ menuOptions: group.menuOptions.map((option) => ({
+ name: option.name,
+ price: option.price.toLocaleString("ko-KR"),
+ })),
+ })) ?? [],
+ optionalOptionGroups:
+ menu?.menuOptionGroups
+ .filter((group) => group.type === "OPTIONAL")
+ .map((group) => ({
+ ...group,
+ menuOptions: group.menuOptions.map((option) => ({
+ name: option.name,
+ price: option.price.toLocaleString("ko-KR"),
+ })),
+ })) ?? [],
+ });
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [menu]);
+
+ const { isSubmitting, handleSubmit } = useMenuFormSubmit({
+ form,
+ menu: menu as MenuDetail,
+ isCreating,
+ close,
});
+ const canEdit = mode !== "detail" && !isSubmitting;
+
+ const handleImageChange = (e: ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (file) {
+ const previewUrl = URL.createObjectURL(file);
+ form.setValue("image", previewUrl);
+ setImageFile(file);
+ }
+ };
+
+ if (!isCreating && !menu?.menuId) return null;
+
return (