From 900c176329f5f81dca7b685df4ece2c7eef9e268 Mon Sep 17 00:00:00 2001 From: hyejj19 Date: Sat, 4 Apr 2026 20:54:04 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[fix]=20=EC=9C=84=EC=8A=A4=ED=82=A4=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EA=B7=B8=EB=A3=B9=202?= =?UTF-8?q?=EB=8B=A8=EA=B3=84=20=EC=84=A0=ED=83=9D=20UI=20=EB=B3=B5?= =?UTF-8?q?=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 아카이브 과정에서 유실된 변경사항 복구: - WhiskyBasicInfoCard: 카테고리 그룹 1차 선택 → 서브카테고리 2차 선택 UI - CategoryReference에서 불필요한 categoryGroup 필드 제거 - mockCategoryReferences 핸들러 추가 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../whisky/components/WhiskyBasicInfoCard.tsx | 122 +++++++++++++----- src/test/mocks/handlers.ts | 6 + src/types/api/alcohol.api.ts | 2 - 3 files changed, 95 insertions(+), 35 deletions(-) diff --git a/src/pages/whisky/components/WhiskyBasicInfoCard.tsx b/src/pages/whisky/components/WhiskyBasicInfoCard.tsx index 82dbee8..60b8e74 100644 --- a/src/pages/whisky/components/WhiskyBasicInfoCard.tsx +++ b/src/pages/whisky/components/WhiskyBasicInfoCard.tsx @@ -3,17 +3,24 @@ * - 한글명, 영문명, 카테고리, 지역, 도수, 증류소 등 기본 정보 폼 */ -import { useMemo } from 'react'; import type { UseFormReturn } from 'react-hook-form'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { FormField } from '@/components/common/FormField'; import { SearchableSelect } from '@/components/common/SearchableSelect'; import type { WhiskyFormValues } from '../whisky.schema'; -import type { CategoryReference } from '@/types/api'; +import type { AlcoholCategory, CategoryReference } from '@/types/api'; +import { ALCOHOL_CATEGORIES, CATEGORY_GROUP_LABELS, GROUP_TO_CATEGORY } from '@/types/api'; /** * WhiskyBasicInfoCard 컴포넌트의 props @@ -40,30 +47,37 @@ export function WhiskyBasicInfoCard({ const { register, watch, setValue, formState } = form; const { errors } = formState; - // 옵션 목록 변환 - const categoryOptions = useMemo( - () => categories.map((cat) => ({ value: cat.korCategory, label: cat.korCategory })), - [categories] - ); + const regionOptions = regions.map((region) => ({ value: String(region.id), label: region.korName })); + const distilleryOptions = distilleries.map((distillery) => ({ value: String(distillery.id), label: distillery.korName })); - const regionOptions = useMemo( - () => regions.map((region) => ({ value: String(region.id), label: region.korName })), - [regions] - ); + const currentCategoryGroup = watch('categoryGroup'); + const isOtherCategory = currentCategoryGroup === 'OTHER'; - const distilleryOptions = useMemo( - () => distilleries.map((distillery) => ({ value: String(distillery.id), label: distillery.korName })), - [distilleries] - ); + // OTHER일 때 기존 서브카테고리 옵션 (CategoryReference API에서 메인 그룹 제외) + const mainKorCategories = new Set(Object.values(GROUP_TO_CATEGORY).map((c) => c.korCategory)); + const otherCategoryOptions = categories + .filter((cat) => !mainKorCategories.has(cat.korCategory)) + .map((cat) => ({ value: cat.korCategory, label: `${cat.korCategory} (${cat.engCategory})` })); + + // 카테고리 그룹 변경 시 korCategory/engCategory 자동 세팅 + const handleCategoryGroupChange = (group: AlcoholCategory) => { + setValue('categoryGroup', group); + if (group !== 'OTHER') { + const mapped = GROUP_TO_CATEGORY[group]; + setValue('korCategory', mapped.korCategory); + setValue('engCategory', mapped.engCategory); + } else { + setValue('korCategory', ''); + setValue('engCategory', ''); + } + }; - // 카테고리 선택 시 korCategory, engCategory, categoryGroup을 함께 저장 - const handleCategoryChange = (korCategory: string) => { - const selected = categories.find((c) => c.korCategory === korCategory); - if (selected) { - setValue('korCategory', selected.korCategory); - setValue('engCategory', selected.engCategory); - // API에서 categoryGroup이 없을 수 있으므로 기본값 'OTHER' 사용 - setValue('categoryGroup', selected.categoryGroup ?? 'OTHER'); + // OTHER 서브카테고리 선택 시 engCategory도 함께 세팅 + const handleOtherCategorySelect = (korCategory: string) => { + setValue('korCategory', korCategory); + const matched = categories.find((c) => c.korCategory === korCategory); + if (matched) { + setValue('engCategory', matched.engCategory); } }; @@ -84,18 +98,60 @@ export function WhiskyBasicInfoCard({ - {/* 카테고리 */} - - + {/* 카테고리 그룹 (1차 선택) */} + + + {/* 카테고리: 메인 그룹은 읽기 전용, OTHER는 선택+입력 */} + {isOtherCategory ? ( + <> + {otherCategoryOptions.length > 0 && ( + + + + )} +
+ + + + + + +
+ + ) : ( + currentCategoryGroup && ( +
+ + + + + + +
+ ) + )} + {/* 지역 / 증류소 */}
diff --git a/src/test/mocks/handlers.ts b/src/test/mocks/handlers.ts index 8c1de21..5a1d150 100644 --- a/src/test/mocks/handlers.ts +++ b/src/test/mocks/handlers.ts @@ -8,6 +8,7 @@ import { mockAlcoholDisconnectionResponse, mockAlcoholListItems, mockAlcoholDeleteResponse, + mockCategoryReferences, mockBannerListItems, mockBannerDetail, mockBannerCreateResponse, @@ -240,6 +241,11 @@ export const bannerHandlers = [ const ALCOHOL_BASE = '/admin/api/v1/alcohols'; export const alcoholHandlers = [ + // GET 카테고리 레퍼런스 (목록보다 먼저 매칭되도록) + http.get(`${ALCOHOL_BASE}/categories/reference`, () => { + return HttpResponse.json(wrapApiResponse(mockCategoryReferences)); + }), + // GET 목록 http.get(ALCOHOL_BASE, ({ request }) => { const url = new URL(request.url); diff --git a/src/types/api/alcohol.api.ts b/src/types/api/alcohol.api.ts index b4ad3c4..ff6459c 100644 --- a/src/types/api/alcohol.api.ts +++ b/src/types/api/alcohol.api.ts @@ -408,6 +408,4 @@ export interface CategoryReference { korCategory: string; /** 영문 카테고리 */ engCategory: string; - /** 카테고리 그룹 (API 응답에 포함되지 않을 수 있음) */ - categoryGroup?: AlcoholCategory; } From 41edb8b5bdd575953ef0dc683b782a704da181f2 Mon Sep 17 00:00:00 2001 From: hyejj19 Date: Sat, 4 Apr 2026 20:54:42 +0900 Subject: [PATCH 2/3] Bump version from 1.2.7 to 1.2.8 Co-Authored-By: Claude Opus 4.6 (1M context) --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c04c650..db6fb4a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.7 +1.2.8 From 362ea9d33dea0cacc8f875168520e937bf19427e Mon Sep 17 00:00:00 2001 From: hyejj19 Date: Sat, 4 Apr 2026 20:57:23 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20CategoryReference=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=EB=90=9C=20categoryGroup=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=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 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/hooks/__tests__/useAdminAlcohols.test.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/hooks/__tests__/useAdminAlcohols.test.ts b/src/hooks/__tests__/useAdminAlcohols.test.ts index 3487c60..3166081 100644 --- a/src/hooks/__tests__/useAdminAlcohols.test.ts +++ b/src/hooks/__tests__/useAdminAlcohols.test.ts @@ -86,17 +86,7 @@ describe('useAdminAlcohols hooks', () => { expect(result.current.data!.length).toBeGreaterThan(0); }); - it('실제 API 응답에는 categoryGroup이 없다', async () => { - const { result } = renderHook(() => useCategoryReferences()); - - await waitFor(() => expect(result.current.isSuccess).toBe(true)); - - // 백엔드 CategoryReference API는 categoryGroup을 내려주지 않음 - result.current.data!.forEach((ref) => { - expect(ref.categoryGroup).toBeUndefined(); - }); - }); - }); +}); // ========================================== // getCategoryGroup (프론트엔드 매핑)