- {title}
+
+ 메뉴 선택이 끝났어요!
+
- {description}
+ 추천 결과를 확인해 보세요
-
diff --git a/src/pageComponents/gathering/opinion/DislikeStep.tsx b/src/pageComponents/gathering/opinion/DislikeStep.tsx
index 2a59585e..8af3a75a 100644
--- a/src/pageComponents/gathering/opinion/DislikeStep.tsx
+++ b/src/pageComponents/gathering/opinion/DislikeStep.tsx
@@ -1,26 +1,19 @@
"use client";
-import { useCallback } from "react";
-import { useFormContext, Controller } from "react-hook-form";
+import { useFormContext, useWatch } from "react-hook-form";
import { Layout } from "#/components/layout";
import { StepIndicator } from "#/components/stepIndicator";
import { StepHeader } from "#/components/stepHeader";
import { Button } from "#/components/button/Button";
-import { FoodCategoryButton } from "./FoodCategoryButton";
+import { DislikedFoodButton } from "./DislikedFoodButton";
import {
FOOD_CATEGORIES,
OPINION_TOTAL_STEPS,
} from "#/constants/gathering/opinion";
-import type {
- OpinionForm,
- FoodCategory,
- DislikeStepProps,
-} from "#/types/gathering";
+import type { OpinionFormSchema } from "#/schemas/gathering";
export const DislikeStepContent = () => {
- const { control } = useFormContext
();
-
return (
@@ -38,66 +31,41 @@ export const DislikeStepContent = () => {
-
{
- const dislikedFoods = field.value || [];
-
- const handleFoodToggle = (food: FoodCategory) => {
- const isSelected = dislikedFoods.includes(food);
- field.onChange(isSelected ? [] : [food]);
- };
-
- return (
-
- {FOOD_CATEGORIES.map((category) => (
-
- handleFoodToggle(category.value)
- }
- label={category.label}
- />
- ))}
-
- );
- }}
- />
+
+ {FOOD_CATEGORIES.map((category) => (
+
+ ))}
+
);
};
-export const DislikeStepFooter = ({
- onNext,
-}: Pick) => {
- const { control } = useFormContext();
+interface DislikeStepFooterProps {
+ onNext: () => void;
+}
- const handleNext = useCallback(() => {
- onNext();
- }, [onNext]);
+export const DislikeStepFooter = ({ onNext }: DislikeStepFooterProps) => {
+ const { control } = useFormContext();
+ const disabled = useWatch({
+ control,
+ name: "dislikedFoods",
+ compute: (value) => !value || value.length === 0,
+ });
return (
- (
-
- )}
- />
+
);
diff --git a/src/pageComponents/gathering/opinion/DislikedFoodButton.tsx b/src/pageComponents/gathering/opinion/DislikedFoodButton.tsx
new file mode 100644
index 00000000..0e9cca00
--- /dev/null
+++ b/src/pageComponents/gathering/opinion/DislikedFoodButton.tsx
@@ -0,0 +1,116 @@
+"use client";
+
+import { useFormContext, useController } from "react-hook-form";
+import { motion } from "motion/react";
+import { XIcon } from "#/icons/xIcon";
+import { cva } from "class-variance-authority";
+import { AnimatePresence } from "motion/react";
+import { twJoin } from "tailwind-merge";
+import Image from "next/image";
+
+import { FOOD_CATEGORY_LABEL } from "#/constants/gathering/opinion";
+import type { FoodCategory } from "#/types/gathering";
+import type { OpinionFormSchema } from "#/schemas/gathering";
+
+const dislikedFoodButtonVariants = cva(
+ [
+ "ygi:flex ygi:flex-col ygi:items-center ygi:justify-center",
+ "ygi:size-[156px] ygi:rounded-full",
+ "ygi:gap-1 ygi:p-6",
+ "ygi:cursor-pointer ygi:transition",
+ "ygi:border ygi:border-solid ygi:bg-surface-lightgray",
+ ],
+ {
+ variants: {
+ isAny: {
+ false: [],
+ true: [],
+ },
+ selected: {
+ false: [
+ "ygi:bg-surface-lightgray-50",
+ "ygi:border-transparent",
+ ],
+ true: ["ygi:border-border-primary", "ygi:bg-surface-primary"],
+ },
+ },
+ compoundVariants: [
+ {
+ isAny: true,
+ selected: true,
+ class: [
+ "ygi:border-border-secondary",
+ "ygi:bg-surface-secondary",
+ ],
+ },
+ ],
+ defaultVariants: {
+ isAny: false,
+ selected: false,
+ },
+ },
+);
+
+interface DislikedFoodButtonProps {
+ category: FoodCategory;
+}
+
+export const DislikedFoodButton = ({ category }: DislikedFoodButtonProps) => {
+ const { control } = useFormContext();
+ const { field } = useController({ name: "dislikedFoods", control });
+
+ const dislikedFoods = field.value || [];
+
+ const isSelected = dislikedFoods.includes(category);
+ const isAny = category === "ANY";
+ const shouldShowXIcon = isSelected && !isAny;
+
+ return (
+
+ );
+};
diff --git a/src/pageComponents/gathering/opinion/DistanceSelector.tsx b/src/pageComponents/gathering/opinion/DistanceSelector.tsx
new file mode 100644
index 00000000..1d48e915
--- /dev/null
+++ b/src/pageComponents/gathering/opinion/DistanceSelector.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import { useFormContext, useController } from "react-hook-form";
+
+import { Chip } from "#/components/chip";
+import { DISTANCE_OPTIONS } from "#/constants/gathering/opinion";
+import type { OpinionFormSchema } from "#/schemas/gathering";
+
+export const DistanceSelector = () => {
+ const { control } = useFormContext();
+ const { field } = useController({ name: "distanceRange", control });
+
+ return (
+
+ {DISTANCE_OPTIONS.map((option) => (
+ {
+ field.onChange(option.value);
+ }}
+ >
+ {option.label}
+
+ ))}
+
+ );
+};
diff --git a/src/pageComponents/gathering/opinion/DistanceStep.tsx b/src/pageComponents/gathering/opinion/DistanceStep.tsx
index 0235ff5b..bba131fc 100644
--- a/src/pageComponents/gathering/opinion/DistanceStep.tsx
+++ b/src/pageComponents/gathering/opinion/DistanceStep.tsx
@@ -1,25 +1,28 @@
"use client";
-import { useCallback } from "react";
-import { useFormContext, useController } from "react-hook-form";
+import { useFormContext, useWatch } from "react-hook-form";
+import { isUndefined } from "es-toolkit/predicate";
import { Layout } from "#/components/layout";
import { StepIndicator } from "#/components/stepIndicator";
import { StepHeader } from "#/components/stepHeader";
import { Button } from "#/components/button";
-import { Chip } from "#/components/chip";
-import { useDistanceStepValidation } from "#/hooks/gathering";
import {
- DISTANCE_OPTIONS,
OPINION_TOTAL_STEPS,
+ REGION_OPTIONS,
} from "#/constants/gathering/opinion";
-import type { OpinionForm, DistanceStepProps } from "#/types/gathering";
+import type { OpinionFormSchema } from "#/schemas/gathering";
+import type { GetGatheringResponse } from "#/apis/gathering";
+import { DistanceSelector } from "./DistanceSelector";
-export const DistanceStepContent = ({
- meetingContext,
-}: Pick) => {
- const { control } = useFormContext();
- const { field } = useController({ name: "distanceRange", control });
+interface DistanceStepContentProps {
+ region: GetGatheringResponse["region"];
+}
+
+export const DistanceStepContent = ({ region }: DistanceStepContentProps) => {
+ const stationName =
+ REGION_OPTIONS.find((currentRegion) => currentRegion.value === region)
+ ?.label ?? "";
return (
@@ -31,41 +34,25 @@ export const DistanceStepContent = ({
괜찮으신지 선택해주세요
- {`${meetingContext.stationName} 기준으로 추천 범위를 정할게요`}
+ {`${stationName} 기준으로 추천 범위를 정할게요`}
-
- {DISTANCE_OPTIONS.map((option) => (
- {
- field.onChange(
- field.value === option.value
- ? undefined
- : option.value,
- );
- }}
- >
- {option.label}
-
- ))}
-
+
);
};
-export const DistanceStepFooter = ({
- onNext,
-}: Pick) => {
- const { control } = useFormContext();
- const isValid = useDistanceStepValidation(control);
+interface DistanceStepFooterProps {
+ onNext: () => void;
+}
- const handleNext = useCallback(() => {
- if (isValid) {
- onNext();
- }
- }, [isValid, onNext]);
+export const DistanceStepFooter = ({ onNext }: DistanceStepFooterProps) => {
+ const { control } = useFormContext();
+ const disabled = useWatch({
+ control,
+ name: "distanceRange",
+ compute: (value) => isUndefined(value),
+ });
return (
@@ -73,8 +60,8 @@ export const DistanceStepFooter = ({
diff --git a/src/pageComponents/gathering/opinion/FoodCard.tsx b/src/pageComponents/gathering/opinion/FoodCard.tsx
index f3c5dee1..2213f455 100644
--- a/src/pageComponents/gathering/opinion/FoodCard.tsx
+++ b/src/pageComponents/gathering/opinion/FoodCard.tsx
@@ -1,9 +1,10 @@
"use client";
import Image from "next/image";
+import type { FoodCategory } from "#/types/gathering";
interface FoodCardProps {
- category: string;
+ category: FoodCategory;
}
export const FoodCard = ({ category }: FoodCardProps) => {
diff --git a/src/pageComponents/gathering/opinion/FoodCategoryButton.tsx b/src/pageComponents/gathering/opinion/FoodCategoryButton.tsx
index e6f29448..d833d157 100644
--- a/src/pageComponents/gathering/opinion/FoodCategoryButton.tsx
+++ b/src/pageComponents/gathering/opinion/FoodCategoryButton.tsx
@@ -68,6 +68,7 @@ export const FoodCategoryButton = ({
return (