Skip to content

Commit f037c79

Browse files
RookieANDclaude
andauthored
feat: 비선호 음식 선택을 최대 2개까지 지정할 수 있도록 제한 (#86)
* feat: 비선호 음식 다중 선택 기능 구현 5개 음식 카테고리 중 최대 4개까지 다중 선택 가능하도록 변경 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> * feat: 비선호 음식 선택 최대 2개 제한 및 상관없음 단독 선택 구현 - 비선호 음식 선택을 최대 2개로 제한 - "상관없음" 선택 시 다른 모든 선택 해제되고 단독 선택 - 일반 음식 선택 시 "상관없음" 자동 해제 - UI에 최대 선택 개수 안내 추가 - 스키마 검증에 max(2) 제약 조건 추가 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> * chore: 불필요한 주석 제거 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> * fix: 코드 리뷰 반영하여 Zod Schema 에 refine 을 등록하여 강화 * fix: 비선호 Category 로직 전반으로 Food 를 Category 로 치환하여 코드 수정 * chore: 불호 선택 버튼 내 Category 텍스트를 Food 로 일괄 수정하여 반영 --------- Co-authored-by: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com>
1 parent bf988c9 commit f037c79

File tree

3 files changed

+39
-13
lines changed

3 files changed

+39
-13
lines changed

src/pageComponents/gathering/opinion/DislikeStep.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const DislikeStepContent = () => {
3636
{FOOD_CATEGORIES.map((category) => (
3737
<DislikedFoodButton
3838
key={category.value}
39-
category={category.value}
39+
food={category.value}
4040
/>
4141
))}
4242
</div>

src/pageComponents/gathering/opinion/DislikedFoodButton.tsx

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import type { OpinionFormSchema } from "#/schemas/gathering";
1515
const dislikedFoodButtonVariants = cva(
1616
[
1717
"ygi:flex ygi:flex-col ygi:items-center ygi:justify-center",
18-
"ygi:size-[156px] ygi:rounded-full",
19-
"ygi:gap-1 ygi:p-6",
18+
"ygi:size-[156px] ygi:gap-1 ygi:rounded-full ygi:p-6",
2019
"ygi:cursor-pointer ygi:transition",
2120
"ygi:border ygi:border-solid ygi:bg-surface-lightgray",
2221
],
@@ -52,19 +51,42 @@ const dislikedFoodButtonVariants = cva(
5251
);
5352

5453
interface DislikedFoodButtonProps {
55-
category: FoodCategory;
54+
food: FoodCategory;
5655
}
5756

58-
export const DislikedFoodButton = ({ category }: DislikedFoodButtonProps) => {
57+
export const DislikedFoodButton = ({ food }: DislikedFoodButtonProps) => {
5958
const { control } = useFormContext<OpinionFormSchema>();
6059
const { field } = useController({ name: "dislikedFoods", control });
6160

62-
const dislikedFoods = field.value || [];
61+
const dislikeFoodList = field.value || [];
6362

64-
const isSelected = dislikedFoods.includes(category);
65-
const isAny = category === "ANY";
63+
const isSelected = dislikeFoodList.includes(food);
64+
const isAny = food === "ANY";
6665
const shouldShowXIcon = isSelected && !isAny;
6766

67+
const handleClickDislikeButton = () => {
68+
if (isSelected) {
69+
field.onChange(
70+
dislikeFoodList.filter((dislikeFood) => dislikeFood !== food),
71+
);
72+
return;
73+
}
74+
75+
if (isAny) {
76+
field.onChange(["ANY"]);
77+
return;
78+
}
79+
80+
const filteredFoods = dislikeFoodList.filter(
81+
(dislikeFood) => dislikeFood !== "ANY",
82+
);
83+
84+
if (filteredFoods.length < 2) {
85+
field.onChange([...filteredFoods, food]);
86+
return;
87+
}
88+
};
89+
6890
return (
6991
<button
7092
type="button"
@@ -73,12 +95,12 @@ export const DislikedFoodButton = ({ category }: DislikedFoodButtonProps) => {
7395
isAny,
7496
selected: isSelected,
7597
})}
76-
onClick={() => field.onChange(isSelected ? [] : [category])}
98+
onClick={handleClickDislikeButton}
7799
>
78100
<div className="ygi:relative ygi:size-20">
79101
<Image
80-
src={`/images/foodCategory/${category.toLowerCase()}.svg`}
81-
alt={FOOD_CATEGORY_LABEL[category]}
102+
src={`/images/foodCategory/${food.toLowerCase()}.svg`}
103+
alt={FOOD_CATEGORY_LABEL[food]}
82104
fill
83105
className="ygi:object-contain"
84106
priority
@@ -109,7 +131,7 @@ export const DislikedFoodButton = ({ category }: DislikedFoodButtonProps) => {
109131
: "ygi:text-text-secondary",
110132
)}
111133
>
112-
{FOOD_CATEGORY_LABEL[category]}
134+
{FOOD_CATEGORY_LABEL[food]}
113135
</span>
114136
</button>
115137
);

src/schemas/gathering/opinionForm.schema.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export const opinionFormSchema = z.object({
2626
distanceRange: distanceRangeSchema,
2727
dislikedFoods: z
2828
.array(foodCategorySchema)
29-
.min(1, "싫어하는 음식을 선택해주세요"),
29+
.min(1, "싫어하는 음식을 선택해주세요")
30+
.max(2, "최대 2개까지 선택 가능합니다")
31+
.refine((foods) => !foods.includes("ANY") || foods.length === 1, {
32+
message: '"상관없음"은 다른 음식과 함께 선택할 수 없습니다.',
33+
}),
3034
preferredMenus: z.object({
3135
first: foodCategorySchema.optional(),
3236
second: foodCategorySchema.optional(),

0 commit comments

Comments
 (0)