From 9d1c84c9bce55c0b7796f61204ee4e7406ef818c Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:42:19 +0900 Subject: [PATCH 01/22] =?UTF-8?q?chore:=20autoplancreate=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/AutoPlanCreate/index.tsx | 146 ------------------ 1 file changed, 146 deletions(-) delete mode 100644 src/components/feature/AutoPlanCreate/index.tsx diff --git a/src/components/feature/AutoPlanCreate/index.tsx b/src/components/feature/AutoPlanCreate/index.tsx deleted file mode 100644 index 1c8239ab..00000000 --- a/src/components/feature/AutoPlanCreate/index.tsx +++ /dev/null @@ -1,146 +0,0 @@ -'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 { SelectedCategory } from '@/type/menuCategory/category'; -import { FailResponse, Result } from '@/type/response'; -import { - getCurrentYearMonthNow, - transformResponseToCalendar, - transformCalendarToPostSave, - isAllFoodsEmpty, -} from '@/utils/calendar'; -import MealForm from '@/components/common/MealForm'; -import MealCalendar from '@/components/shared/Meal/MealCalender'; -import MealCreateHeader from '@/components/shared/Meal/MealCreateHeader'; -import { MealHeaderFormData } from '@/components/shared/Meal/MealHeader'; -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 { usePostMonthMenusSave } from '@/hooks/menu/usePostMonthMenusSave'; -import useNavigate from '@/hooks/useNavigate'; -import { useAutoPlanStore } from '@/stores/useAutoPlanStore'; -import { useToastStore } from '@/stores/useToastStore'; - -const AutoPlanCreate = () => { - const [calendarData, setCalendarData] = useState({}); - const [selectedDate, setSelectedDate] = useState(''); - const [selectedCategory, setSelectedCategory] = useState({ - majorCategory: '', - minorCategory: '', - }); - const { year, month } = getCurrentYearMonthNow(); - const showToast = useToastStore((state) => state.showToast); - const { navigate } = useNavigate(); - const { setMonthMenuName, category } = useAutoPlanStore((state) => ({ - category: state.category, - setMonthMenuName: state.setMonthMenuName, - })); - - const queryClient = useQueryClient(); - const { mutate: postSaveMutate } = usePostMonthMenusSave(); - - const { - register, - handleSubmit, - getValues, - formState: { errors }, - } = useForm({ - resolver: zodResolver(mealHeaderSchema), - mode: 'onChange', - }); - - const handleDateClick = (date: string) => { - setSelectedDate(date); - }; - - const handleEditMenu = () => { - const inputData = getValues(); - setMonthMenuName(inputData.monthMenuName); - navigate(ROUTES.EDIT.AUTO); - }; - - const onSubmit = (data: MealHeaderFormData) => { - if (isAllFoodsEmpty(calendarData)) { - showToast('빈 식단은 생성할 수 없습니다.', 'warning', 3000); - return; - } - - 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); - }, - }); - }; - - const onError = () => { - if (errors.monthMenuName) { - showToast(MEAL_HEADER_ERROR.name.min, 'warning', 3000); - return; - } - }; - - useEffect(() => { - const menus = queryClient.getQueryData(['monthMenusAuto']); - if (!menus) return; - const calendarData = transformResponseToCalendar(year, month, menus); - setSelectedCategory({ - majorCategory: category.majorCategory, - minorCategory: category.minorCategory, - }); - setCalendarData(calendarData); - }, [ - year, - month, - queryClient, - category.majorCategory, - category.minorCategory, - ]); - - return ( - - - - - ); -}; - -export default AutoPlanCreate; From 48f726491564f787dc7c7db029a24c4d0f91182c Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:42:29 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20api=20endpoint=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/_apiPath.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/_apiPath.ts b/src/constants/_apiPath.ts index b747a052..3e05434e 100644 --- a/src/constants/_apiPath.ts +++ b/src/constants/_apiPath.ts @@ -52,6 +52,7 @@ export const MENU_CAGEGORY_API = { MENU_CATEGORIES: BASE_API.MENU_CATEGORIES, MAJOR_CATEGORY: 'major-category', MINOR_CATEGORY: 'minor-category', + SEARCH_SCHOOL: 'search-school', }; /** From f17edeef94fefef203e12952f161d074014b49d3 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:42:45 +0900 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EC=BF=BC=EB=A6=AC=ED=82=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/menuCategory/queryKey.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/hooks/menuCategory/queryKey.ts diff --git a/src/hooks/menuCategory/queryKey.ts b/src/hooks/menuCategory/queryKey.ts new file mode 100644 index 00000000..41b3e2f6 --- /dev/null +++ b/src/hooks/menuCategory/queryKey.ts @@ -0,0 +1,7 @@ +import { GetSearchSchoolRequest } from '@/type/menuCategory/menuCategoryRequest'; + +export const menuCategoryKeys = { + all: ['menu-categories'] as const, + searchSchool: (request?: GetSearchSchoolRequest) => + [...menuCategoryKeys.all, request?.keyword] as const, +}; From 6cf72a0361277903a01784df49a9d855b5bac4a1 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:43:00 +0900 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20api=20=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/menuCategory/index.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/api/menuCategory/index.ts b/src/api/menuCategory/index.ts index 37a14701..69c3ecc0 100644 --- a/src/api/menuCategory/index.ts +++ b/src/api/menuCategory/index.ts @@ -1,5 +1,6 @@ import { get } from '@/lib/axios'; import { MajorCategory } from '@/type/menu/menuRequest'; +import { GetSearchSchoolRequest } from '@/type/menuCategory/menuCategoryRequest'; import { Result } from '@/type/response'; import { MENU_CAGEGORY_API } from '@/constants/_apiPath'; @@ -14,6 +15,19 @@ const getMinorCategories = async (param: MajorCategory) => { return response.data; }; +/** + * @description 학교명 검색 api + */ +const getSearchSchool = async ({ keyword }: GetSearchSchoolRequest) => { + const response = await get>(MENU_CATEGORIES, { + params: { + keyword, + }, + }); + return response.data; +}; + export const menuCategories = { getMinorCategories, + getSearchSchool, }; From 9d88972dbbed3a04a4dbc1bdf16cbd968c82ac21 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:43:16 +0900 Subject: [PATCH 05/22] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20api=20=EB=A6=AC=ED=80=98=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/type/menuCategory/menuCategoryRequest.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/type/menuCategory/menuCategoryRequest.ts diff --git a/src/type/menuCategory/menuCategoryRequest.ts b/src/type/menuCategory/menuCategoryRequest.ts new file mode 100644 index 00000000..8ed5095d --- /dev/null +++ b/src/type/menuCategory/menuCategoryRequest.ts @@ -0,0 +1,3 @@ +export type GetSearchSchoolRequest = { + keyword: string; +}; From fb2b0913032aead8523cd908b65505a30f286214 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:43:28 +0900 Subject: [PATCH 06/22] =?UTF-8?q?feat:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=ED=9B=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/menuCategory/useGetSearchSchool.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/hooks/menuCategory/useGetSearchSchool.ts diff --git a/src/hooks/menuCategory/useGetSearchSchool.ts b/src/hooks/menuCategory/useGetSearchSchool.ts new file mode 100644 index 00000000..cdd5e780 --- /dev/null +++ b/src/hooks/menuCategory/useGetSearchSchool.ts @@ -0,0 +1,11 @@ +import { useQuery } from '@tanstack/react-query'; +import { menuCategories } from '@/api/menuCategory'; +import { GetSearchSchoolRequest } from '@/type/menuCategory/menuCategoryRequest'; +import { menuCategoryKeys } from '@/hooks/menuCategory/queryKey'; + +export const useGetSearchSchool = (request: GetSearchSchoolRequest) => { + return useQuery({ + queryKey: menuCategoryKeys.searchSchool(request), + queryFn: () => menuCategories.getSearchSchool(request), + }); +}; From 6414d6f05b4e455063eff56983fc55a81d1fa003 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Tue, 25 Mar 2025 13:43:39 +0900 Subject: [PATCH 07/22] =?UTF-8?q?temp:=20todo=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feature/AutoPlan/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/feature/AutoPlan/index.tsx b/src/components/feature/AutoPlan/index.tsx index 40a8dd7b..a3f85e87 100644 --- a/src/components/feature/AutoPlan/index.tsx +++ b/src/components/feature/AutoPlan/index.tsx @@ -200,6 +200,9 @@ const AutoPlan = () => { )} + {/* TODO: 학교명 선택시 학교명 검색 api + input창, 검색 결과 표시할 dropdown 필요. + */}
Date: Sat, 5 Apr 2025 21:28:11 +0900 Subject: [PATCH 08/22] =?UTF-8?q?fix:=20search=20school=20api=20path=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/_apiPath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/_apiPath.ts b/src/constants/_apiPath.ts index 3e05434e..8d7e5b27 100644 --- a/src/constants/_apiPath.ts +++ b/src/constants/_apiPath.ts @@ -52,7 +52,7 @@ export const MENU_CAGEGORY_API = { MENU_CATEGORIES: BASE_API.MENU_CATEGORIES, MAJOR_CATEGORY: 'major-category', MINOR_CATEGORY: 'minor-category', - SEARCH_SCHOOL: 'search-school', + SEARCH_SCHOOL: `${BASE_API.MENU_CATEGORIES}/search-school`, }; /** From 999d64da1070fc58a80ffdc0fe07fbe7cc9abb75 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:28:37 +0900 Subject: [PATCH 09/22] =?UTF-8?q?fix:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20api=20response=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/menuCategory/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/menuCategory/index.ts b/src/api/menuCategory/index.ts index 69c3ecc0..81fd64c1 100644 --- a/src/api/menuCategory/index.ts +++ b/src/api/menuCategory/index.ts @@ -4,7 +4,7 @@ import { GetSearchSchoolRequest } from '@/type/menuCategory/menuCategoryRequest' import { Result } from '@/type/response'; import { MENU_CAGEGORY_API } from '@/constants/_apiPath'; -const { MENU_CATEGORIES } = MENU_CAGEGORY_API; +const { MENU_CATEGORIES, SEARCH_SCHOOL } = MENU_CAGEGORY_API; const getMinorCategories = async (param: MajorCategory) => { const response = await get>(MENU_CATEGORIES, { @@ -19,7 +19,7 @@ const getMinorCategories = async (param: MajorCategory) => { * @description 학교명 검색 api */ const getSearchSchool = async ({ keyword }: GetSearchSchoolRequest) => { - const response = await get>(MENU_CATEGORIES, { + const response = await get>(SEARCH_SCHOOL, { params: { keyword, }, From 5b1b593cdff7b89897f24bd75f461ce2d25e7593 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:30:33 +0900 Subject: [PATCH 10/22] =?UTF-8?q?feat:=20category=20mappings=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=95=99=EA=B5=90=EB=AA=85=20=EC=98=B5=EC=85=98=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/_category.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/_category.ts b/src/constants/_category.ts index 797361df..3092b8c1 100644 --- a/src/constants/_category.ts +++ b/src/constants/_category.ts @@ -27,6 +27,6 @@ export const MOCK_CATEGORY_LIST = [ export const CATEGORY_MAPPINGS = [ { category: MAJOR_CATEGORIES[0], queryKey: 'getSchoolMinorCategories' }, - { category: MAJOR_CATEGORIES[1], queryKey: 'getSchoolNameMinorCategories' }, + // { category: MAJOR_CATEGORIES[1], queryKey: 'getSchoolNameMinorCategories' }, { category: MAJOR_CATEGORIES[2], queryKey: 'getHospitalMinorCategories' }, ] as const; From b0b225a3faf4e0bbba3def4989c496dd5c866284 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:30:51 +0900 Subject: [PATCH 11/22] =?UTF-8?q?feat:=20=EC=B5=9C=EC=86=8C=20=ED=95=99?= =?UTF-8?q?=EA=B5=90=EB=AA=85=20=EA=B2=80=EC=83=89=20=EA=B8=B8=EC=9D=B4=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/_MealForm.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/constants/_MealForm.ts b/src/constants/_MealForm.ts index 09b9f6a2..46f247f8 100644 --- a/src/constants/_MealForm.ts +++ b/src/constants/_MealForm.ts @@ -11,3 +11,5 @@ export const MEAL_FORM_LEGEND = { edit: '식단 수정', }, }; + +export const MINIMUM_SCHOOL_NAME_LENGTH = 2; From 39172f2aaa5d6c90253cc2168ecf2676bc6c840e Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:31:19 +0900 Subject: [PATCH 12/22] =?UTF-8?q?feat:=20string=20array=20to=20option=20ar?= =?UTF-8?q?ray=20=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/meal.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/utils/meal.ts b/src/utils/meal.ts index df127ce0..21f31a9f 100644 --- a/src/utils/meal.ts +++ b/src/utils/meal.ts @@ -1,10 +1,10 @@ +import { Option } from '@/components/common/Selectbox'; + /** * @description 소수점이 전부 0인 숫자들 정수로 변환 (메뉴 영양 정보 값) * @example 84.000 -> 84 - * @param value - * @returns */ -export function removeTrailingZeros(value: number): number { +export const removeTrailingZeros = (value: number): number => { const stringValue = typeof value === 'number' ? value.toString() : value; if (stringValue.includes('.')) { @@ -15,4 +15,14 @@ export function removeTrailingZeros(value: number): number { } return Number(stringValue); -} +}; + +/** + * @description api response로 받은 string 배열을 selectbox options 타입으로 변환 + * @example ['A학교', 'B학교'] -> [{value: 'A학교', label: 'A학교'} , {value: 'B학교', label: 'B학교'}] + */ +export const stringArraytoOptionArray = (items: string[]): Option[] => + items.map((item) => ({ + value: item, + label: item, + })); From dbe3397ddb1f7cb698b859b4fe356f8c7c9561da Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:31:38 +0900 Subject: [PATCH 13/22] =?UTF-8?q?feat:=20meal=20header=20form=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/type/mealType.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/type/mealType.ts b/src/type/mealType.ts index 0cdcf24d..a12382ac 100644 --- a/src/type/mealType.ts +++ b/src/type/mealType.ts @@ -6,3 +6,8 @@ export type CalendarInfo = { foods: FoodInfo[]; }; }; + +export type MealHeaderForm = { + monthMenuName: string; + schoolName: string; +}; From 876e0f4e6169da720b16c751a1443520ca7cca85 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:32:06 +0900 Subject: [PATCH 14/22] =?UTF-8?q?fix:=20=ED=95=99=EA=B5=90=EB=AA=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20use=20=ED=9B=85=EC=97=90=20options=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/menuCategory/useGetSearchSchool.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hooks/menuCategory/useGetSearchSchool.ts b/src/hooks/menuCategory/useGetSearchSchool.ts index cdd5e780..4dd57371 100644 --- a/src/hooks/menuCategory/useGetSearchSchool.ts +++ b/src/hooks/menuCategory/useGetSearchSchool.ts @@ -3,9 +3,15 @@ import { menuCategories } from '@/api/menuCategory'; import { GetSearchSchoolRequest } from '@/type/menuCategory/menuCategoryRequest'; import { menuCategoryKeys } from '@/hooks/menuCategory/queryKey'; -export const useGetSearchSchool = (request: GetSearchSchoolRequest) => { +export const useGetSearchSchool = ( + request: GetSearchSchoolRequest, + options?: { + enabled?: boolean; + }, +) => { return useQuery({ queryKey: menuCategoryKeys.searchSchool(request), queryFn: () => menuCategories.getSearchSchool(request), + ...options, }); }; From d4df8f218374a21c44620891196c80e8a9bd6941 Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sat, 5 Apr 2025 21:37:35 +0900 Subject: [PATCH 15/22] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EC=8B=9D?= =?UTF-8?q?=EB=8B=A8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20-=20=ED=95=99?= =?UTF-8?q?=EA=B5=90=EB=AA=85=20=EA=B2=80=EC=83=89=20api=20=EB=B0=94?= =?UTF-8?q?=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feature/AutoPlan/index.tsx | 111 ++++++++++++++++++++-- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/src/components/feature/AutoPlan/index.tsx b/src/components/feature/AutoPlan/index.tsx index a3f85e87..c25cea51 100644 --- a/src/components/feature/AutoPlan/index.tsx +++ b/src/components/feature/AutoPlan/index.tsx @@ -6,7 +6,7 @@ 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 { CalendarInfo, MealHeaderForm } from '@/type/mealType'; import { MajorCategory } from '@/type/menu/menuRequest'; import { MenuResponse } from '@/type/menu/menuResponse'; import { @@ -21,10 +21,13 @@ import { transformCalendarToPostSave, transformResponseToCalendar, } from '@/utils/calendar'; +import { stringArraytoOptionArray } from '@/utils/meal'; import Button from '@/components/common/Button/Button'; +import Dropdown from '@/components/common/Dropdown'; import { Input } from '@/components/common/Input'; import MealForm from '@/components/common/MealForm'; -import { Selectbox } from '@/components/common/Selectbox'; +import OptionList from '@/components/common/OptionList'; +import { Option, Selectbox } from '@/components/common/Selectbox'; import { Caption1Grey500, H2BlackH2, @@ -34,13 +37,17 @@ import MealCalendar from '@/components/shared/Meal/MealCalender'; 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 { + MEAL_FORM_LEGEND, + MINIMUM_SCHOOL_NAME_LENGTH, +} 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 { useGetSearchSchool } from '@/hooks/menuCategory/useGetSearchSchool'; import { usePrefetchMinorCategories } from '@/hooks/menuCategory/usePrefetchMinorCategories'; import useNavigate from '@/hooks/useNavigate'; import { useToastStore } from '@/stores/useToastStore'; @@ -56,6 +63,9 @@ const AutoPlan = () => { minorCategory: '', }); const [isCategoryError, setIsCategoryError] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [options, setOptions] = useState([]); + const [keyword, setKeyword] = useState(''); const { year, month } = getCurrentYearMonthNow(); const showToast = useToastStore((state) => state.showToast); const { navigate } = useNavigate(); @@ -73,7 +83,10 @@ const AutoPlan = () => { register, handleSubmit, formState: { errors }, - } = useForm({ + watch, + getValues, + setValue, + } = useForm({ resolver: zodResolver(mealHeaderSchema), mode: 'onChange', }); @@ -173,6 +186,60 @@ const AutoPlan = () => { prefetchMinorCategories(); }, [hasCategories, prefetchMinorCategories]); + const handleSchoolNameSelect = (schoolName: string) => { + setValue('schoolName', schoolName); + setSelectedCategory((prev) => ({ + ...prev, + minorCategory: schoolName, + })); + setIsOpen(false); + }; + + const { + data: schoolNameData, + isSuccess: isSchoolNameSuccess, + isError: isSchoolNameError, + } = useGetSearchSchool( + { + keyword: keyword, + }, + { + enabled: keyword.length >= MINIMUM_SCHOOL_NAME_LENGTH, + }, + ); + + // 학교명 입력 후 검색 클릭 시 + const handleSearchClick = () => { + if (getValues('schoolName').length < MINIMUM_SCHOOL_NAME_LENGTH) + return null; + + setKeyword(getValues('schoolName')); + + if (isSchoolNameError) { + showToast( + '학교명 검색에 실패했습니다. 잠시 후 다시 시도해주세요.', + 'warning', + ); + setIsOpen(false); + return null; + } + }; + + useEffect(() => { + if (isSchoolNameSuccess) { + if (schoolNameData.data.length === 0) { + showToast('찾으시는 학교명이 없습니다.', 'warning'); + setIsOpen(false); + return; + } + const formattedOptions = stringArraytoOptionArray(schoolNameData.data); + setOptions(formattedOptions); + setIsOpen(true); + } + }, [isSchoolNameSuccess, schoolNameData]); + + const watchSchoolName = watch('schoolName'); + return (
{
)} - {/* TODO: 학교명 선택시 학교명 검색 api - input창, 검색 결과 표시할 dropdown 필요. - */}
{ selectedValue={selectedCategory.majorCategory} isError={isCategoryError} /> +
+ {selectedCategory.majorCategory === '학교명' && ( +
+ + +
+ )} + + + +
+ {ORGANIZATION_LIST.map( (organization) => - selectedCategory.majorCategory === organization.value && ( + selectedCategory.majorCategory === organization.value && + selectedCategory.majorCategory !== '학교명' && ( Date: Sat, 5 Apr 2025 23:28:03 +0900 Subject: [PATCH 16/22] =?UTF-8?q?feat:=20=EC=8B=9D=EB=8B=A8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=8E=98=EC=9D=B4=EC=A7=80=20-=20meal=20header=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=ED=95=B4=EC=A0=9C,=20=ED=95=99=EA=B5=90?= =?UTF-8?q?=EB=AA=85=20=EA=B2=80=EC=83=89=20api=20=EB=B0=94=EC=9D=B8?= =?UTF-8?q?=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feature/MealPlanEdit/index.tsx | 198 ++++++++++++++++-- 1 file changed, 181 insertions(+), 17 deletions(-) diff --git a/src/components/feature/MealPlanEdit/index.tsx b/src/components/feature/MealPlanEdit/index.tsx index c56e5c52..4be12228 100644 --- a/src/components/feature/MealPlanEdit/index.tsx +++ b/src/components/feature/MealPlanEdit/index.tsx @@ -6,7 +6,7 @@ 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 { CalendarInfo, MealHeaderForm } from '@/type/mealType'; import { MajorCategory } from '@/type/menu/menuRequest'; import { FoodInfo, MenuResponseDTO } from '@/type/menu/menuResponse'; import { @@ -20,17 +20,33 @@ import { transformCalendarToPostSave, transformResponseToCalendar, } from '@/utils/calendar'; +import { stringArraytoOptionArray } from '@/utils/meal'; +import Button from '@/components/common/Button/Button'; +import Dropdown from '@/components/common/Dropdown'; +import { Input } from '@/components/common/Input'; import MealForm from '@/components/common/MealForm'; +import OptionList from '@/components/common/OptionList'; +import { Option, Selectbox } from '@/components/common/Selectbox'; +import { + H2BlackH2, + Subtitle2Green500, + Subtitle2Grey900, + Subtitle2White, +} from '@/components/common/Typography'; import MealCalendar from '@/components/shared/Meal/MealCalender'; -import MealHeader, { - MealHeaderFormData, -} from '@/components/shared/Meal/MealHeader'; -import { MEAL_FORM_LEGEND } from '@/constants/_MealForm'; +import { MealHeaderFormData } from '@/components/shared/Meal/MealHeader'; +import { ORGANIZATION_LIST } from '@/constants/_category'; +import { MAJOR_CATEGORIES } from '@/constants/_meal'; +import { + MEAL_FORM_LEGEND, + MINIMUM_SCHOOL_NAME_LENGTH, +} from '@/constants/_MealForm'; import { ROUTES } from '@/constants/_navbar'; import { PAGE_TITLE } from '@/constants/_pageTitle'; import { MEAL_HEADER_ERROR } from '@/constants/_schema'; import { usePutMonthMenus } from '@/hooks/menu/usePutMonthMenus'; import { useFetchMinorCategories } from '@/hooks/menuCategory/useFetchMinorCategories'; +import { useGetSearchSchool } from '@/hooks/menuCategory/useGetSearchSchool'; import useNavigate from '@/hooks/useNavigate'; import { useToastStore } from '@/stores/useToastStore'; @@ -38,6 +54,9 @@ type MealPlanEditProps = { id: string; }; +/** + * @description 식단 수정 페이지 + */ const MealPlanEdit = ({ id: monthMenuId }: MealPlanEditProps) => { const [cachedCalendarData, setCachedCalendarData] = useState( {}, @@ -51,8 +70,11 @@ const MealPlanEdit = ({ id: monthMenuId }: MealPlanEditProps) => { minorCategory: '', }); const [isCategoryError, setIsCategoryError] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [options, setOptions] = useState([]); + const [keyword, setKeyword] = useState(''); const showToast = useToastStore((state) => state.showToast); - const { navigate } = useNavigate(); + const { navigate, handleBack } = useNavigate(); const queryClient = useQueryClient(); const { minorCategories } = useFetchMinorCategories( @@ -65,11 +87,15 @@ const MealPlanEdit = ({ id: monthMenuId }: MealPlanEditProps) => { handleSubmit, formState: { errors }, reset, - } = useForm({ + watch, + getValues, + setValue, + } = useForm({ resolver: zodResolver(mealHeaderSchema), mode: 'onChange', defaultValues: { monthMenuName: '', + schoolName: '', }, }); @@ -179,6 +205,7 @@ const MealPlanEdit = ({ id: monthMenuId }: MealPlanEditProps) => { } = cachedMealPlanData.data; reset({ monthMenuName: monthMenuName, + schoolName: minorCategory, }); const { year, month } = getYearAndMonth(createAt); setYear(year); @@ -201,6 +228,60 @@ const MealPlanEdit = ({ id: monthMenuId }: MealPlanEditProps) => { getOriginalCalendar(); }, []); + const handleSchoolNameSelect = (schoolName: string) => { + setValue('schoolName', schoolName); + setSelectedCategory((prev) => ({ + ...prev, + minorCategory: schoolName, + })); + setIsOpen(false); + }; + + const { + data: schoolNameData, + isSuccess: isSchoolNameSuccess, + isError: isSchoolNameError, + } = useGetSearchSchool( + { + keyword: keyword, + }, + { + enabled: keyword.length >= MINIMUM_SCHOOL_NAME_LENGTH, + }, + ); + + // 학교명 입력 후 검색 클릭 시 + const handleSearchClick = () => { + if (getValues('schoolName').length < MINIMUM_SCHOOL_NAME_LENGTH) + return null; + + setKeyword(getValues('schoolName')); + + if (isSchoolNameError) { + showToast( + '학교명 검색에 실패했습니다. 잠시 후 다시 시도해주세요.', + 'warning', + ); + setIsOpen(false); + return null; + } + }; + + useEffect(() => { + if (isSchoolNameSuccess) { + if (schoolNameData.data.length === 0) { + showToast('찾으시는 학교명이 없습니다.', 'warning'); + setIsOpen(false); + return; + } + const formattedOptions = stringArraytoOptionArray(schoolNameData.data); + setOptions(formattedOptions); + setIsOpen(true); + } + }, [isSchoolNameSuccess, schoolNameData]); + + const watchSchoolName = watch('schoolName'); + if (!calendarData) { return
로딩 중...
; } @@ -210,16 +291,99 @@ const MealPlanEdit = ({ id: monthMenuId }: MealPlanEditProps) => { legend={MEAL_FORM_LEGEND.mealPlan.edit} handleSubmit={handleSubmit(onSubmit, onError)} > - +
+
+ {PAGE_TITLE.mealPlan.edit} +
+
+ {register && errors && ( +
+ +
+ )} + +
+ { + handleChangeCategory('majorCategory', majorCategory); + }} + selectedValue={selectedCategory.majorCategory} + isError={isCategoryError} + /> +
+ {selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && ( +
+ + +
+ )} + + + +
+ {ORGANIZATION_LIST.map( + (organization) => + selectedCategory.majorCategory === organization.value && + selectedCategory.majorCategory !== MAJOR_CATEGORIES[1] && ( + + handleChangeCategory('minorCategory', minorCategory) + } + selectedValue={selectedCategory.minorCategory} + isError={isCategoryError} + /> + ), + )} +
+ +
+
+ + +
+ +
+
+
Date: Sat, 5 Apr 2025 23:28:28 +0900 Subject: [PATCH 17/22] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EC=8B=9D?= =?UTF-8?q?=EB=8B=A8=20=20=ED=8E=98=EC=9D=B4=EC=A7=80=20-=20meal=20header?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=20=ED=95=B4=EC=A0=9C,=20=ED=95=99?= =?UTF-8?q?=EA=B5=90=EB=AA=85=20=EA=B2=80=EC=83=89=20api=20=EB=B0=94?= =?UTF-8?q?=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feature/MenualPlan/index.tsx | 198 +++++++++++++++++--- 1 file changed, 176 insertions(+), 22 deletions(-) diff --git a/src/components/feature/MenualPlan/index.tsx b/src/components/feature/MenualPlan/index.tsx index 83fd3ff5..b8022f8a 100644 --- a/src/components/feature/MenualPlan/index.tsx +++ b/src/components/feature/MenualPlan/index.tsx @@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { mealHeaderSchema } from '@/schema/mealSchema'; -import { CalendarInfo } from '@/type/mealType'; +import { CalendarInfo, MealHeaderForm } from '@/type/mealType'; import { FoodInfo } from '@/type/menu/menuResponse'; import { HandleChangeCategoryParam, @@ -15,17 +15,28 @@ import { hasFoods, isValidDateString, } from '@/utils/calendar'; +import { stringArraytoOptionArray } from '@/utils/meal'; +import Button from '@/components/common/Button/Button'; +import Dropdown from '@/components/common/Dropdown'; +import { Input } from '@/components/common/Input'; import MealForm from '@/components/common/MealForm'; +import OptionList from '@/components/common/OptionList'; +import { Option, Selectbox } from '@/components/common/Selectbox'; +import { H2BlackH2, Subtitle2White } from '@/components/common/Typography'; import MealCalendar from '@/components/shared/Meal/MealCalender'; -import MealHeader, { - MealHeaderFormData, -} from '@/components/shared/Meal/MealHeader'; -import { MEAL_FORM_LEGEND } from '@/constants/_MealForm'; +import { MealHeaderFormData } from '@/components/shared/Meal/MealHeader'; +import { ORGANIZATION_LIST } from '@/constants/_category'; +import { MAJOR_CATEGORIES } from '@/constants/_meal'; +import { + MEAL_FORM_LEGEND, + MINIMUM_SCHOOL_NAME_LENGTH, +} from '@/constants/_MealForm'; import { ROUTES } from '@/constants/_navbar'; import { PAGE_TITLE } from '@/constants/_pageTitle'; import { MEAL_HEADER_ERROR } from '@/constants/_schema'; import { MEAL_CREATE_MESSAGE } from '@/constants/_toastMessage'; import { useFetchMinorCategories } from '@/hooks/menuCategory/useFetchMinorCategories'; +import { useGetSearchSchool } from '@/hooks/menuCategory/useGetSearchSchool'; import { usePrefetchMinorCategories } from '@/hooks/menuCategory/usePrefetchMinorCategories'; import useNavigate from '@/hooks/useNavigate'; import { useMenualPlanStore } from '@/stores/useMenualPlanStore'; @@ -38,9 +49,13 @@ const MenualPlan = () => { majorCategory: '', minorCategory: '', }); - const { year, month } = getCurrentYearMonthNow(); const [isCategoryError, setIsCategoryError] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [options, setOptions] = useState([]); + const [keyword, setKeyword] = useState(''); + const { year, month } = getCurrentYearMonthNow(); const showToast = useToastStore((state) => state.showToast); + const { navigate } = useNavigate(); const { setMonthMenuName, setCategory, setCalendar } = useMenualPlanStore( (state) => ({ setMonthMenuName: state.setMonthMenuName, @@ -48,7 +63,6 @@ const MenualPlan = () => { setCalendar: state.setCalendar, }), ); - const { navigate } = useNavigate(); const { minorCategories } = useFetchMinorCategories( selectedCategory.majorCategory, @@ -60,7 +74,10 @@ const MenualPlan = () => { register, handleSubmit, formState: { errors }, - } = useForm({ + watch, + getValues, + setValue, + } = useForm({ resolver: zodResolver(mealHeaderSchema), mode: 'onChange', }); @@ -90,10 +107,13 @@ const MenualPlan = () => { if (isValidDateString(date)) setSelectedDate(date); }; - const handleResetMenu = () => { - setCalendarData({}); - setSelectedDate(''); - }; + const isBothSelected = + selectedCategory.majorCategory && selectedCategory.minorCategory; + + // const handleResetMenu = () => { + // setCalendarData({}); + // setSelectedDate(''); + // }; const onSubmit = (data: MealHeaderFormData) => { const { majorCategory, minorCategory } = selectedCategory; @@ -129,22 +149,156 @@ const MenualPlan = () => { prefetchMinorCategories(); }, [hasCategories, prefetchMinorCategories]); + const handleSchoolNameSelect = (schoolName: string) => { + setValue('schoolName', schoolName); + setSelectedCategory((prev) => ({ + ...prev, + minorCategory: schoolName, + })); + setIsOpen(false); + }; + + const { + data: schoolNameData, + isSuccess: isSchoolNameSuccess, + isError: isSchoolNameError, + } = useGetSearchSchool( + { + keyword: keyword, + }, + { + enabled: keyword.length >= MINIMUM_SCHOOL_NAME_LENGTH, + }, + ); + + // 학교명 입력 후 검색 클릭 시 + const handleSearchClick = () => { + if (getValues('schoolName').length < MINIMUM_SCHOOL_NAME_LENGTH) + return null; + + setKeyword(getValues('schoolName')); + + if (isSchoolNameError) { + showToast( + '학교명 검색에 실패했습니다. 잠시 후 다시 시도해주세요.', + 'warning', + ); + setIsOpen(false); + return null; + } + }; + + useEffect(() => { + if (isSchoolNameSuccess) { + if (schoolNameData.data.length === 0) { + showToast('찾으시는 학교명이 없습니다.', 'warning'); + setIsOpen(false); + return; + } + const formattedOptions = stringArraytoOptionArray(schoolNameData.data); + setOptions(formattedOptions); + setIsOpen(true); + } + }, [isSchoolNameSuccess, schoolNameData]); + + const watchSchoolName = watch('schoolName'); + return (
- +
+
+ {PAGE_TITLE.menualPlan.default} +
+
+ {register && errors && ( +
+ +
+ )} + +
+ { + handleChangeCategory('majorCategory', majorCategory); + }} + selectedValue={selectedCategory.majorCategory} + isError={isCategoryError} + /> + +
+ {selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && ( +
+ + +
+ )} + + + +
+ + {ORGANIZATION_LIST.map( + (item) => + selectedCategory.majorCategory === item.value && ( + + handleChangeCategory('minorCategory', minorCategory) + } + selectedValue={selectedCategory.minorCategory} + isError={isCategoryError} + /> + ), + )} +
+ + +
+
+ Date: Sat, 5 Apr 2025 23:28:57 +0900 Subject: [PATCH 18/22] =?UTF-8?q?fix:=20=EC=9E=90=EB=8F=99=EC=8B=9D?= =?UTF-8?q?=EB=8B=A8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20-=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EB=B3=80=EC=88=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feature/AutoPlan/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/feature/AutoPlan/index.tsx b/src/components/feature/AutoPlan/index.tsx index c25cea51..b2d5edf6 100644 --- a/src/components/feature/AutoPlan/index.tsx +++ b/src/components/feature/AutoPlan/index.tsx @@ -36,7 +36,7 @@ import { import MealCalendar from '@/components/shared/Meal/MealCalender'; import { MealHeaderFormData } from '@/components/shared/Meal/MealHeader'; import { ORGANIZATION_LIST } from '@/constants/_category'; -import { AUTO_PLAN_BETA_MESSAGE } from '@/constants/_meal'; +import { AUTO_PLAN_BETA_MESSAGE, MAJOR_CATEGORIES } from '@/constants/_meal'; import { MEAL_FORM_LEGEND, MINIMUM_SCHOOL_NAME_LENGTH, @@ -279,7 +279,7 @@ const AutoPlan = () => { isError={isCategoryError} />
- {selectedCategory.majorCategory === '학교명' && ( + {selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && (
{ {ORGANIZATION_LIST.map( (organization) => selectedCategory.majorCategory === organization.value && - selectedCategory.majorCategory !== '학교명' && ( + selectedCategory.majorCategory !== MAJOR_CATEGORIES[1] && ( Date: Sun, 6 Apr 2025 00:07:26 +0900 Subject: [PATCH 19/22] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EC=8B=9D?= =?UTF-8?q?=EB=8B=A8=20=EC=88=98=EC=A0=95=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?-=20=ED=95=99=EA=B5=90=EB=AA=85=20=EA=B2=80=EC=83=89=20api=20?= =?UTF-8?q?=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/MenualPlanEdit/index.tsx | 187 ++++++++++++++++-- 1 file changed, 171 insertions(+), 16 deletions(-) diff --git a/src/components/feature/MenualPlanEdit/index.tsx b/src/components/feature/MenualPlanEdit/index.tsx index 13fc9d9e..be7d4212 100644 --- a/src/components/feature/MenualPlanEdit/index.tsx +++ b/src/components/feature/MenualPlanEdit/index.tsx @@ -1,11 +1,11 @@ 'use client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { AxiosError } from 'axios'; import { useForm } from 'react-hook-form'; import { mealHeaderSchema } from '@/schema/mealSchema'; -import { CalendarInfo } from '@/type/mealType'; +import { CalendarInfo, MealHeaderForm } from '@/type/mealType'; import { MajorCategory } from '@/type/menu/menuRequest'; import { FoodInfo } from '@/type/menu/menuResponse'; import { @@ -18,17 +18,28 @@ import { isValidDateString, transformCalendarToPostSave, } from '@/utils/calendar'; +import { stringArraytoOptionArray } from '@/utils/meal'; +import Button from '@/components/common/Button/Button'; +import Dropdown from '@/components/common/Dropdown'; +import { Input } from '@/components/common/Input'; import MealForm from '@/components/common/MealForm'; +import OptionList from '@/components/common/OptionList'; +import { Option, Selectbox } from '@/components/common/Selectbox'; +import { H2BlackH2, Subtitle2White } from '@/components/common/Typography'; import MealCalendar from '@/components/shared/Meal/MealCalender'; -import MealHeader, { - MealHeaderFormData, -} from '@/components/shared/Meal/MealHeader'; -import { MEAL_FORM_LEGEND } from '@/constants/_MealForm'; +import { MealHeaderFormData } from '@/components/shared/Meal/MealHeader'; +import { ORGANIZATION_LIST } from '@/constants/_category'; +import { MAJOR_CATEGORIES } from '@/constants/_meal'; +import { + MEAL_FORM_LEGEND, + MINIMUM_SCHOOL_NAME_LENGTH, +} from '@/constants/_MealForm'; import { ROUTES } from '@/constants/_navbar'; import { PAGE_TITLE } from '@/constants/_pageTitle'; import { MEAL_HEADER_ERROR } from '@/constants/_schema'; import { usePostMonthMenusSave } from '@/hooks/menu/usePostMonthMenusSave'; import { useFetchMinorCategories } from '@/hooks/menuCategory/useFetchMinorCategories'; +import { useGetSearchSchool } from '@/hooks/menuCategory/useGetSearchSchool'; import useNavigate from '@/hooks/useNavigate'; import { useMenualPlanStore } from '@/stores/useMenualPlanStore'; import { useToastStore } from '@/stores/useToastStore'; @@ -45,9 +56,15 @@ const MenualPlanEdit = () => { useState(category); const { year, month } = getCurrentYearMonthNow(); const [isCategoryError, setIsCategoryError] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [options, setOptions] = useState([]); + const [keyword, setKeyword] = useState(''); const showToast = useToastStore((state) => state.showToast); const { navigate } = useNavigate(); + const isBothSelected = + selectedCategory.majorCategory && selectedCategory.minorCategory; + const { minorCategories } = useFetchMinorCategories( selectedCategory.majorCategory, ); @@ -57,11 +74,15 @@ const MenualPlanEdit = () => { register, handleSubmit, formState: { errors }, - } = useForm({ + watch, + getValues, + setValue, + } = useForm({ resolver: zodResolver(mealHeaderSchema), mode: 'onChange', defaultValues: { monthMenuName, + schoolName: category.minorCategory, }, }); @@ -140,20 +161,154 @@ const MenualPlanEdit = () => { } }; + const handleSchoolNameSelect = (schoolName: string) => { + setValue('schoolName', schoolName); + setSelectedCategory((prev) => ({ + ...prev, + minorCategory: schoolName, + })); + setIsOpen(false); + }; + + const { + data: schoolNameData, + isSuccess: isSchoolNameSuccess, + isError: isSchoolNameError, + } = useGetSearchSchool( + { + keyword: keyword, + }, + { + enabled: keyword.length >= MINIMUM_SCHOOL_NAME_LENGTH, + }, + ); + + // 학교명 입력 후 검색 클릭 시 + const handleSearchClick = () => { + if (getValues('schoolName').length < MINIMUM_SCHOOL_NAME_LENGTH) + return null; + + setKeyword(getValues('schoolName')); + + if (isSchoolNameError) { + showToast( + '학교명 검색에 실패했습니다. 잠시 후 다시 시도해주세요.', + 'warning', + ); + setIsOpen(false); + return null; + } + }; + + useEffect(() => { + if (isSchoolNameSuccess) { + if (schoolNameData.data.length === 0) { + showToast('찾으시는 학교명이 없습니다.', 'warning'); + setIsOpen(false); + return; + } + const formattedOptions = stringArraytoOptionArray(schoolNameData.data); + setOptions(formattedOptions); + setIsOpen(true); + } + }, [isSchoolNameSuccess, schoolNameData]); + + const watchSchoolName = watch('schoolName'); + return ( - +
+
+ {PAGE_TITLE.menualPlan.edit} +
+
+ {register && errors && ( +
+ +
+ )} + +
+ { + handleChangeCategory('majorCategory', majorCategory); + }} + selectedValue={selectedCategory.majorCategory} + isError={isCategoryError} + /> +
+ {selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && ( +
+ + +
+ )} + + + +
+ + {ORGANIZATION_LIST.map( + (organization) => + selectedCategory.majorCategory === organization.value && + selectedCategory.majorCategory !== MAJOR_CATEGORIES[1] && ( + + handleChangeCategory('minorCategory', minorCategory) + } + selectedValue={selectedCategory.minorCategory} + isError={isCategoryError} + /> + ), + )} +
+ + +
+
Date: Sun, 6 Apr 2025 00:07:56 +0900 Subject: [PATCH 20/22] =?UTF-8?q?fix:=20=EC=9E=90=EB=8F=99=EC=8B=9D?= =?UTF-8?q?=EB=8B=A8,=20=EC=88=98=EB=8F=99=EC=8B=9D=EB=8B=A8=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=88=98=EB=8F=99=EC=8B=9D=EB=8B=A8=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20-=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/feature/AutoPlan/index.tsx | 2 +- src/components/feature/MealPlanEdit/index.tsx | 2 +- src/components/feature/MenualPlan/index.tsx | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/feature/AutoPlan/index.tsx b/src/components/feature/AutoPlan/index.tsx index b2d5edf6..a65ca786 100644 --- a/src/components/feature/AutoPlan/index.tsx +++ b/src/components/feature/AutoPlan/index.tsx @@ -280,7 +280,7 @@ const AutoPlan = () => { />
{selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && ( -
+
{ />
{selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && ( -
+
{
{selectedCategory.majorCategory === MAJOR_CATEGORIES[1] && ( -
+
{ {ORGANIZATION_LIST.map( (item) => - selectedCategory.majorCategory === item.value && ( + selectedCategory.majorCategory === item.value && + selectedCategory.majorCategory !== MAJOR_CATEGORIES[1] && ( Date: Sun, 6 Apr 2025 16:01:21 +0900 Subject: [PATCH 21/22] =?UTF-8?q?fix:=20transformResponseToCalender=20?= =?UTF-8?q?=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/calendar.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/utils/calendar.ts b/src/utils/calendar.ts index 9ff58a04..84768789 100644 --- a/src/utils/calendar.ts +++ b/src/utils/calendar.ts @@ -93,7 +93,6 @@ export const transformResponseToCalendar = ( apiData.forEach((menu, index) => { const currentDate = startOfMonth.add(index, 'day'); - if (currentDate.month() !== month - 1) return; const formattedDate = formatFullDate(currentDate); @@ -113,12 +112,18 @@ export const transformResponseToCalendar = ( fat: food.fat, })); - if (filteredFoods.length > 0) { + const menuDate = (menu as MonthMenu).menuDate; + + if (filteredFoods.length === 0) return null; + + if (type === 'auto') { calendarData[formattedDate] = { - menuId: - type === 'auto' - ? (menu as MenuResponse).menuId - : (menu as MonthMenu).menuId, + menuId: (menu as MenuResponse).menuId, + foods: filteredFoods, + }; + } else { + calendarData[menuDate] = { + menuId: (menu as MonthMenu).menuId, foods: filteredFoods, }; } From 97f7348f8a1ce1166051b844b60129df480d783c Mon Sep 17 00:00:00 2001 From: grapefruit Date: Sun, 6 Apr 2025 16:01:44 +0900 Subject: [PATCH 22/22] =?UTF-8?q?fix:=20Calendar=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20data=20=ED=94=84=EB=A1=AD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/Calendar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/Calendar/index.tsx b/src/components/common/Calendar/index.tsx index 8c3656e1..063d976f 100644 --- a/src/components/common/Calendar/index.tsx +++ b/src/components/common/Calendar/index.tsx @@ -57,7 +57,7 @@ const Calendar = ({ date={date.format('D')} isHoliday={isHoliday(date)} isInvalid={isInvalidDate(date, year, month)} - data={data?.[formattedDate as keyof CalendarInfo]?.foods || []} + data={data?.[formattedDate]?.foods || []} isActive={isActive} index={index} totalDays={allDays.length}