diff --git a/src/app/(root)/autoPlan/create/page.tsx b/src/app/(root)/autoPlan/create/page.tsx deleted file mode 100644 index a43a600c..00000000 --- a/src/app/(root)/autoPlan/create/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import AutoPlanCreate from '@/components/feature/AutoPlanCreate'; -import { PAGE_METADATA } from '@/constants/_metadata'; - -const { title, description } = PAGE_METADATA.AUTOPLAN.CREATE; - -export const metadata = { - title, - description, -}; - -const page = () => { - return ; -}; - -export default page; diff --git a/src/components/feature/AutoPlan/index.tsx b/src/components/feature/AutoPlan/index.tsx index 3d93e15d..40a8dd7b 100644 --- a/src/components/feature/AutoPlan/index.tsx +++ b/src/components/feature/AutoPlan/index.tsx @@ -1,29 +1,56 @@ 'use client'; import { useEffect, useState } from 'react'; +import { zodResolver } from '@hookform/resolvers/zod'; import { useQueryClient } from '@tanstack/react-query'; import { AxiosError } from 'axios'; +import { useForm } from 'react-hook-form'; +import { mealHeaderSchema } from '@/schema/mealSchema'; +import { CalendarInfo } from '@/type/mealType'; +import { MajorCategory } from '@/type/menu/menuRequest'; +import { MenuResponse } from '@/type/menu/menuResponse'; import { HandleChangeCategoryParam, SelectedCategory, } from '@/type/menuCategory/category'; import { FailResponse, Result } from '@/type/response'; -import { getCurrentYearMonthNow, getLastDateOfMonth } from '@/utils/calendar'; +import { + getCurrentYearMonthNow, + getLastDateOfMonth, + isAllFoodsEmpty, + transformCalendarToPostSave, + transformResponseToCalendar, +} from '@/utils/calendar'; +import Button from '@/components/common/Button/Button'; +import { Input } from '@/components/common/Input'; import MealForm from '@/components/common/MealForm'; +import { Selectbox } from '@/components/common/Selectbox'; +import { + Caption1Grey500, + H2BlackH2, + Subtitle2White, +} from '@/components/common/Typography'; import MealCalendar from '@/components/shared/Meal/MealCalender'; -import MealHeader from '@/components/shared/Meal/MealHeader'; +import { MealHeaderFormData } from '@/components/shared/Meal/MealHeader'; +import { ORGANIZATION_LIST } from '@/constants/_category'; +import { AUTO_PLAN_BETA_MESSAGE } from '@/constants/_meal'; import { MEAL_FORM_LEGEND } from '@/constants/_MealForm'; import { ROUTES } from '@/constants/_navbar'; import { PAGE_TITLE } from '@/constants/_pageTitle'; import { MEAL_HEADER_ERROR } from '@/constants/_schema'; import { usePostMonthMenusAuto } from '@/hooks/menu/usePostMonthMenusAuto'; +import { usePostMonthMenusSave } from '@/hooks/menu/usePostMonthMenusSave'; import { useFetchMinorCategories } from '@/hooks/menuCategory/useFetchMinorCategories'; import { usePrefetchMinorCategories } from '@/hooks/menuCategory/usePrefetchMinorCategories'; import useNavigate from '@/hooks/useNavigate'; -import { useAutoPlanStore } from '@/stores/useAutoPlanStore'; import { useToastStore } from '@/stores/useToastStore'; +/** + * @description 자동 식단 생성 페이지 + */ const AutoPlan = () => { + const [calendarData, setCalendarData] = useState({}); + const [selectedDate, setSelectedDate] = useState(''); const [selectedCategory, setSelectedCategory] = useState({ majorCategory: '', minorCategory: '', @@ -32,16 +59,25 @@ const AutoPlan = () => { const { year, month } = getCurrentYearMonthNow(); const showToast = useToastStore((state) => state.showToast); const { navigate } = useNavigate(); - const setCategory = useAutoPlanStore((state) => state.setCategory); const queryClient = useQueryClient(); const { minorCategories } = useFetchMinorCategories( selectedCategory.majorCategory, ); const { mutate: postAutoMutate } = usePostMonthMenusAuto(); + const { mutate: postSaveMutate } = usePostMonthMenusSave(); const { prefetchMinorCategories, hasCategories } = usePrefetchMinorCategories(); + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(mealHeaderSchema), + mode: 'onChange', + }); + const handleChangeCategory = ( type: HandleChangeCategoryParam, value: string, @@ -49,17 +85,23 @@ const AutoPlan = () => { setSelectedCategory((prev) => ({ ...prev, [type]: value, + ...(type === 'majorCategory' && { minorCategory: '' }), })); setIsCategoryError(false); }; - const handleSubmit = () => { - const { majorCategory, minorCategory } = selectedCategory; + const handleDateClick = (date: string) => { + setSelectedDate(date); + }; - const isSelectedCategoryInvalid = - majorCategory === '' || minorCategory === ''; + const isBothSelected = + selectedCategory.majorCategory && selectedCategory.minorCategory; - if (isSelectedCategoryInvalid) { + // 식단 생성 + const handleCreatePlan = () => { + const { majorCategory, minorCategory } = selectedCategory; + + if (!isBothSelected) { showToast(MEAL_HEADER_ERROR.category.min, 'warning', 3000); setIsCategoryError(true); return; @@ -67,19 +109,25 @@ const AutoPlan = () => { postAutoMutate( { - majorCategory: majorCategory, - minorCategory: minorCategory, + majorCategory, + minorCategory, dayCount: getLastDateOfMonth(year, month), }, { onSuccess: ({ message, data }: Result) => { showToast(message, 'success', 1000); queryClient.setQueryData(['monthMenusAuto'], data); - setCategory({ - majorCategory, - minorCategory, - }); - navigate(ROUTES.CREATE.AUTO); + const menus = queryClient.getQueryData([ + 'monthMenusAuto', + ]); + if (menus) { + const calendarData = transformResponseToCalendar( + year, + month, + menus, + ); + setCalendarData(calendarData); + } }, onError: (error: AxiosError) => { const errorMessage = @@ -89,6 +137,37 @@ const AutoPlan = () => { }, ); }; + + // 저장 버튼 클릭 시 + const onSubmit = (data: MealHeaderFormData) => { + const formattedData = transformCalendarToPostSave( + calendarData, + data.monthMenuName, + selectedCategory.majorCategory as MajorCategory, + selectedCategory.minorCategory, + ); + + postSaveMutate(formattedData, { + onSuccess: ({ message }: Result) => { + showToast(message, 'success', 1000); + navigate(ROUTES.VIEW.PLAN); + }, + onError: (error: AxiosError) => { + const errorMessage = + error?.response?.data?.message || '자동 식단 저장 실패'; + showToast(errorMessage, 'warning', 1000); + }, + }); + }; + + // form 유효성 검사 실패 시 + const onError = () => { + if (errors.monthMenuName) { + showToast(MEAL_HEADER_ERROR.name.min, 'warning', 3000); + return; + } + }; + useEffect(() => { if (hasCategories) return; prefetchMinorCategories(); @@ -98,21 +177,86 @@ const AutoPlan = () => {
- +
+
+ {PAGE_TITLE.autoPlan.default} + {AUTO_PLAN_BETA_MESSAGE} +
+
+
+ {register && errors && ( +
+ +
+ )} + +
+ { + handleChangeCategory('majorCategory', majorCategory); + }} + selectedValue={selectedCategory.majorCategory} + isError={isCategoryError} + /> + {ORGANIZATION_LIST.map( + (organization) => + selectedCategory.majorCategory === organization.value && ( + + handleChangeCategory('minorCategory', minorCategory) + } + selectedValue={selectedCategory.minorCategory} + isError={isCategoryError} + /> + ), + )} +
+ +
+
+ +
+
+