Radio
개요
Radio는 사용자가 여러 옵션 중 하나만 선택할 수 있는 폼 컴포넌트입니다. Cals 예약 관리 시스템에서는 예약 타입 선택, 결제 방법 선택, 시간대 선택 등 단일 선택이 필요한 상황에서 활용됩니다.
Cals 특화 기능:
- 예약 상태별 색상 시스템으로 선택 옵션 시각화
- 예약 타입(즉시/승인 필요) 선택 최적화
- 결제 방법 및 서비스 패키지 단일 선택
- 시간대 및 날짜 선택을 위한 라디오 그룹
설치
npx @vortex/cli add radio-group --package cals기본 사용법
import "@vortex/ui-cals/theme";
import { RadioGroup, RadioGroupItem } from "@vortex/ui-cals";
export default function ReservationType() {
return (
<RadioGroup defaultValue="instant">
<div className="flex items-center space-x-2">
<RadioGroupItem value="instant" id="instant" />
<label htmlFor="instant">즉시 예약</label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="approval" id="approval" />
<label htmlFor="approval">승인 후 예약</label>
</div>
</RadioGroup>
);
}Variants
Default
기본 스타일의 Radio입니다.
<RadioGroup defaultValue="option1">
<div className="flex items-center space-x-2">
<RadioGroupItem value="option1" id="option1" />
<label htmlFor="option1">옵션 1</label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option2" id="option2" />
<label htmlFor="option2">옵션 2</label>
</div>
</RadioGroup>With Label
레이블과 함께 사용하는 Radio입니다.
<RadioGroup defaultValue="option1">
<div className="flex items-center space-x-2">
<RadioGroupItem value="option1" id="r-option1" />
<label htmlFor="r-option1" className="text-sm font-medium">
레이블이 있는 옵션 1
</label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option2" id="r-option2" />
<label htmlFor="r-option2" className="text-sm font-medium">
레이블이 있는 옵션 2
</label>
</div>
</RadioGroup>With Description
설명과 함께 사용하는 Radio입니다.
<RadioGroup defaultValue="option1">
<div className="flex items-start space-x-2">
<RadioGroupItem value="option1" id="desc-option1" className="mt-1" />
<div>
<label htmlFor="desc-option1" className="text-sm font-medium">
옵션 제목
</label>
<p className="text-sm text-gray-500">옵션에 대한 상세 설명</p>
</div>
</div>
</RadioGroup>Sizes
Small
작은 크기의 Radio입니다.
<RadioGroupItem value="small" className="h-4 w-4" />Medium (Default)
기본 크기의 Radio입니다.
<RadioGroupItem value="medium" className="h-5 w-5" />Large
큰 크기의 Radio입니다.
<RadioGroupItem value="large" className="h-6 w-6" />States
Default
기본 상태의 Radio입니다.
<RadioGroupItem value="default" />Selected
선택된 Radio입니다.
<RadioGroup value="selected">
<RadioGroupItem value="selected" />
</RadioGroup>Disabled
비활성화된 Radio입니다.
<RadioGroupItem value="disabled" disabled />Disabled Selected
비활성화되고 선택된 Radio입니다.
<RadioGroup value="disabled-selected">
<RadioGroupItem value="disabled-selected" disabled />
</RadioGroup>🆕 Cals 브랜딩
브랜드 컬러
Cals Radio는 기본적으로 Cals의 Primary 색상(Pink #e91e63)을 선택 상태에서 사용합니다.
<div className="space-y-4">
{/* Primary (Pink) - 기본 선택 */}
<RadioGroup defaultValue="primary">
<div className="flex items-center space-x-2">
<RadioGroupItem
value="primary"
id="primary"
className="border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<label htmlFor="primary">Primary (Pink) - 기본 서비스</label>
</div>
</RadioGroup>
{/* Secondary (Blue) - 정보성 선택 */}
<RadioGroup defaultValue="secondary">
<div className="flex items-center space-x-2">
<RadioGroupItem
value="secondary"
id="secondary"
className="border-[#03a9f4] data-[state=checked]:bg-[#03a9f4] data-[state=checked]:border-[#03a9f4]"
/>
<label htmlFor="secondary">Secondary (Blue) - 알림 방식</label>
</div>
</RadioGroup>
{/* Accent (Purple) - 강조 선택 */}
<RadioGroup defaultValue="accent">
<div className="flex items-center space-x-2">
<RadioGroupItem
value="accent"
id="accent"
className="border-[#9c27b0] data-[state=checked]:bg-[#9c27b0] data-[state=checked]:border-[#9c27b0]"
/>
<label htmlFor="accent">Accent (Purple) - 프리미엄 패키지</label>
</div>
</RadioGroup>
</div>예약 상태 컬러
Cals Radio는 예약 관리의 5가지 상태를 반영하여 선택 옵션의 시각적 피드백을 제공합니다.
<RadioGroup defaultValue="available">
<div className="space-y-3">
{/* Available - 선택 가능 */}
<div className="flex items-center space-x-2">
<RadioGroupItem
value="available"
id="status-available"
className="border-[#4caf50] data-[state=checked]:bg-[#4caf50] data-[state=checked]:border-[#4caf50]"
/>
<label htmlFor="status-available" className="text-sm">
<span className="font-medium">예약 가능</span>
<span className="text-gray-500"> - 즉시 예약 가능한 시간</span>
</label>
</div>
{/* Pending - 승인 필요 */}
<div className="flex items-center space-x-2">
<RadioGroupItem
value="pending"
id="status-pending"
disabled
className="border-[#ff9800] data-[state=checked]:bg-[#ff9800] data-[state=checked]:border-[#ff9800]"
/>
<label htmlFor="status-pending" className="text-sm text-gray-500">
<span className="font-medium">승인 대기</span>
<span> - 담당자 승인 필요</span>
</label>
</div>
{/* Confirmed - 확정됨 */}
<div className="flex items-center space-x-2">
<RadioGroupItem
value="confirmed"
id="status-confirmed"
disabled
className="border-[#03a9f4] data-[state=checked]:bg-[#03a9f4] data-[state=checked]:border-[#03a9f4]"
/>
<label htmlFor="status-confirmed" className="text-sm text-gray-500">
<span className="font-medium">예약 확정</span>
<span> - 이미 예약된 시간</span>
</label>
</div>
{/* Cancelled - 취소됨 */}
<div className="flex items-center space-x-2">
<RadioGroupItem
value="cancelled"
id="status-cancelled"
disabled
className="border-[#f44336] data-[state=checked]:bg-[#f44336] data-[state=checked]:border-[#f44336]"
/>
<label
htmlFor="status-cancelled"
className="text-sm text-gray-500 line-through"
>
<span className="font-medium">예약 취소</span>
<span> - 취소된 시간</span>
</label>
</div>
{/* Completed - 완료됨 */}
<div className="flex items-center space-x-2">
<RadioGroupItem
value="completed"
id="status-completed"
disabled
className="border-[#9c27b0] data-[state=checked]:bg-[#9c27b0] data-[state=checked]:border-[#9c27b0]"
/>
<label htmlFor="status-completed" className="text-sm text-gray-500">
<span className="font-medium">서비스 완료</span>
<span> - 완료된 서비스</span>
</label>
</div>
</div>
</RadioGroup>Cals 특화 사용 가이드
예약 타입 선택
<div className="space-y-3">
<h3 className="text-sm font-medium text-gray-900">
예약 방식 선택 <span className="text-[#e91e63]">*</span>
</h3>
<RadioGroup defaultValue="instant">
<div className="flex items-start space-x-3 p-3 border border-gray-200 rounded-md hover:border-[#e91e63] transition-colors">
<RadioGroupItem
value="instant"
id="instant"
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label htmlFor="instant" className="text-sm font-medium cursor-pointer">
즉시 예약
</label>
<p className="text-xs text-gray-500 mt-1">
선택 즉시 예약이 확정됩니다. 별도 승인 과정 없이 바로 서비스를 받을 수
있습니다.
</p>
</div>
</div>
<div className="flex items-start space-x-3 p-3 border border-gray-200 rounded-md hover:border-[#e91e63] transition-colors">
<RadioGroupItem
value="approval"
id="approval"
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label
htmlFor="approval"
className="text-sm font-medium cursor-pointer"
>
승인 후 예약
</label>
<p className="text-xs text-gray-500 mt-1">
담당자가 예약을 검토한 후 승인됩니다. 승인까지 최대 1시간 소요될 수
있습니다.
</p>
</div>
</div>
</RadioGroup>
</div>결제 방법 선택
<div className="space-y-3">
<h3 className="text-sm font-medium text-gray-900">
결제 방법 <span className="text-[#e91e63]">*</span>
</h3>
<RadioGroup defaultValue="card">
<div className="flex items-center space-x-3 p-3 border border-gray-200 rounded-md">
<RadioGroupItem
value="card"
id="card"
className="border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<label
htmlFor="card"
className="text-sm font-medium cursor-pointer flex-1"
>
신용/체크카드
</label>
<span className="text-xs text-[#4caf50] font-medium">추천</span>
</div>
<div className="flex items-center space-x-3 p-3 border border-gray-200 rounded-md">
<RadioGroupItem
value="cash"
id="cash"
className="border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<label htmlFor="cash" className="text-sm font-medium cursor-pointer">
현장 결제 (현금)
</label>
</div>
<div className="flex items-center space-x-3 p-3 border border-gray-200 rounded-md">
<RadioGroupItem
value="transfer"
id="transfer"
className="border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<label htmlFor="transfer" className="text-sm font-medium cursor-pointer">
계좌 이체
</label>
</div>
</RadioGroup>
</div>서비스 패키지 선택
<div className="space-y-3">
<h3 className="text-sm font-medium text-gray-900">
서비스 패키지 선택 <span className="text-[#e91e63]">*</span>
</h3>
<RadioGroup defaultValue="basic">
<div className="flex items-start space-x-3 p-4 border-2 border-gray-200 rounded-lg hover:border-[#e91e63] transition-colors">
<RadioGroupItem
value="basic"
id="basic"
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label htmlFor="basic" className="text-sm font-medium cursor-pointer">
기본 패키지
</label>
<p className="text-xs text-gray-500 mt-1">컷 + 샴푸 (50분)</p>
<p className="text-base font-semibold text-[#e91e63] mt-2">₩40,000</p>
</div>
</div>
<div className="flex items-start space-x-3 p-4 border-2 border-[#9c27b0] bg-purple-50 rounded-lg">
<RadioGroupItem
value="premium"
id="premium"
className="mt-1 border-[#9c27b0] data-[state=checked]:bg-[#9c27b0] data-[state=checked]:border-[#9c27b0]"
/>
<div className="flex-1">
<label htmlFor="premium" className="text-sm font-medium cursor-pointer">
프리미엄 패키지
</label>
<p className="text-xs text-gray-500 mt-1">
컷 + 펌 + 트리트먼트 + 샴푸 (150분)
</p>
<div className="flex items-center gap-2 mt-2">
<p className="text-base font-semibold text-[#9c27b0]">₩180,000</p>
<span className="text-xs text-gray-500 line-through">₩200,000</span>
<span className="text-xs text-[#9c27b0] font-medium">10% 할인</span>
</div>
</div>
<span className="text-xs bg-[#9c27b0] text-white px-2 py-1 rounded-full">
인기
</span>
</div>
<div className="flex items-start space-x-3 p-4 border-2 border-gray-200 rounded-lg hover:border-[#e91e63] transition-colors">
<RadioGroupItem
value="deluxe"
id="deluxe"
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label htmlFor="deluxe" className="text-sm font-medium cursor-pointer">
디럭스 패키지
</label>
<p className="text-xs text-gray-500 mt-1">
컷 + 염색 + 클리닉 + 트리트먼트 + 샴푸 (180분)
</p>
<p className="text-base font-semibold text-[#e91e63] mt-2">₩280,000</p>
</div>
</div>
</RadioGroup>
</div>🆕 Foundation/iCignal과의 차이점
| 속성 | Foundation | iCignal | Cals |
|---|---|---|---|
| Primary Color | #3B82F6 (Blue) | #2196f3 (Blue) | #e91e63 (Pink) |
| Secondary Color | #10B981 (Green) | #4caf50 (Green) | #03a9f4 (Blue) |
| Accent Color | #8B5CF6 (Purple) | #ff5722 (Deep Orange) | #9c27b0 (Purple) |
| 사용 맥락 | 범용 웹 애플리케이션 | Analytics 대시보드 | 예약 관리 시스템 |
| 예약 상태 | 없음 | 없음 | 5가지 상태 (Available/Pending/Confirmed/Cancelled/Completed) |
| 주요 사용 | 범용 단일 선택 | 필터 옵션 선택 | 예약 타입, 결제 방법, 서비스 패키지 선택 |
| 선택 스타일 | Blue background | Blue background | Pink background (Primary) |
| 카드형 레이아웃 | 기본 레이아웃 | 없음 | 패키지 선택 카드, 상세 설명 포함 |
사용 시나리오 비교
Foundation Radio: 범용 단일 선택, 설정 옵션, 항목 선택 iCignal Radio: 데이터 필터, 차트 타입 선택, 분석 기간 선택 Cals Radio: 예약 타입 선택, 결제 방법 선택, 서비스 패키지 선택, 시간대 선택
Props API
RadioGroup
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | 선택된 값 (제어 컴포넌트) |
defaultValue | string | - | 기본 선택 값 (비제어 컴포넌트) |
onValueChange | (value: string) => void | - | 값 변경 이벤트 핸들러 |
disabled | boolean | false | 전체 그룹 비활성화 여부 |
name | string | - | Form name 속성 |
required | boolean | false | 필수 선택 여부 |
className | string | - | 추가 CSS 클래스 |
RadioGroupItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Radio 값 (필수) |
id | string | - | HTML id 속성 (label 연결용) |
disabled | boolean | false | 비활성화 여부 |
className | string | - | 추가 CSS 클래스 |
접근성
Radio 컴포넌트는 웹 접근성 표준을 준수합니다:
- Radix UI 기반: WAI-ARIA 1.2 Radio Group 패턴 구현
- 시맨틱 마크업:
role="radiogroup"및role="radio"사용 - 키보드 네비게이션:
Arrow Up/Down또는Arrow Left/Right: 옵션 간 이동 및 선택Tab: 다음 Radio Group으로 이동Shift + Tab: 이전 Radio Group으로 이동Space: 현재 옵션 선택
- 스크린 리더:
aria-checked상태 안내 - Label 연결:
htmlFor와id를 통한 label-radio 연결 - Focus 관리: Roving tabindex로 그룹 내 포커스 관리
- Required 표시:
aria-required="true"지원
접근성 예제
<div>
<h3 id="payment-method" className="text-sm font-medium text-gray-900 mb-2">
결제 방법{" "}
<span className="text-[#e91e63]" aria-label="필수">
*
</span>
</h3>
<RadioGroup aria-labelledby="payment-method" required aria-required="true">
<div className="flex items-center space-x-2">
<RadioGroupItem value="card" id="payment-card" />
<label htmlFor="payment-card" className="text-sm">
신용/체크카드
</label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="cash" id="payment-cash" />
<label htmlFor="payment-cash" className="text-sm">
현장 결제
</label>
</div>
</RadioGroup>
</div>예제
예약 타입 선택 폼
import { RadioGroup, RadioGroupItem } from "@vortex/ui-cals";
import { useState } from "react";
export default function ReservationTypeForm() {
const [reservationType, setReservationType] = useState("instant");
return (
<div className="space-y-4 max-w-md">
<h3 className="text-base font-semibold text-gray-900">
예약 방식 선택 <span className="text-[#e91e63]">*</span>
</h3>
<RadioGroup value={reservationType} onValueChange={setReservationType}>
<div className="flex items-start space-x-3 p-4 border-2 border-gray-200 rounded-lg hover:border-[#e91e63] transition-colors cursor-pointer">
<RadioGroupItem
value="instant"
id="instant-reservation"
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label
htmlFor="instant-reservation"
className="text-sm font-medium cursor-pointer"
>
즉시 예약
</label>
<p className="text-xs text-gray-500 mt-1">
선택 즉시 예약이 확정됩니다. 별도 승인 과정 없이 바로 서비스를
받을 수 있습니다.
</p>
<div className="flex items-center gap-2 mt-2">
<span className="text-xs bg-[#4caf50] text-white px-2 py-0.5 rounded">
빠른 예약
</span>
<span className="text-xs text-gray-500">
평균 처리 시간: 즉시
</span>
</div>
</div>
</div>
<div className="flex items-start space-x-3 p-4 border-2 border-gray-200 rounded-lg hover:border-[#e91e63] transition-colors cursor-pointer">
<RadioGroupItem
value="approval"
id="approval-reservation"
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label
htmlFor="approval-reservation"
className="text-sm font-medium cursor-pointer"
>
승인 후 예약
</label>
<p className="text-xs text-gray-500 mt-1">
담당자가 예약을 검토한 후 승인됩니다. 승인까지 최대 1시간 소요될
수 있습니다.
</p>
<div className="flex items-center gap-2 mt-2">
<span className="text-xs bg-[#ff9800] text-white px-2 py-0.5 rounded">
승인 필요
</span>
<span className="text-xs text-gray-500">
평균 처리 시간: 1시간 이내
</span>
</div>
</div>
</div>
</RadioGroup>
{reservationType === "instant" && (
<div className="p-3 bg-green-50 rounded-md border border-green-200">
<p className="text-sm text-green-800">
즉시 예약이 가능한 시간입니다. 예약 후 바로 서비스를 받으실 수
있습니다.
</p>
</div>
)}
{reservationType === "approval" && (
<div className="p-3 bg-orange-50 rounded-md border border-orange-200">
<p className="text-sm text-orange-800">
담당자 승인 후 예약이 확정됩니다. SMS/이메일로 승인 알림을 받으실 수
있습니다.
</p>
</div>
)}
</div>
);
}결제 방법 선택 폼
import { RadioGroup, RadioGroupItem } from "@vortex/ui-cals";
import { CreditCard, Banknote, Building2 } from "lucide-react";
import { useState } from "react";
export default function PaymentMethodForm() {
const [paymentMethod, setPaymentMethod] = useState("card");
const methods = [
{
value: "card",
label: "신용/체크카드",
description: "즉시 결제되며, 모든 카드사 사용 가능",
icon: CreditCard,
badge: "추천",
badgeColor: "bg-[#4caf50]",
},
{
value: "cash",
label: "현장 결제 (현금)",
description: "서비스 완료 후 현금으로 결제",
icon: Banknote,
badge: null,
},
{
value: "transfer",
label: "계좌 이체",
description: "예약 후 24시간 내 입금 확인",
icon: Building2,
badge: null,
},
];
return (
<div className="space-y-4 max-w-md">
<h3 className="text-base font-semibold text-gray-900">
결제 방법 <span className="text-[#e91e63]">*</span>
</h3>
<RadioGroup value={paymentMethod} onValueChange={setPaymentMethod}>
{methods.map((method) => {
const Icon = method.icon;
return (
<div
key={method.value}
className={`
flex items-center space-x-3 p-4 border-2 rounded-lg transition-all cursor-pointer
${
paymentMethod === method.value
? "border-[#e91e63] bg-pink-50"
: "border-gray-200 hover:border-[#e91e63]"
}
`}
>
<RadioGroupItem
value={method.value}
id={method.value}
className="border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<Icon className="h-5 w-5 text-gray-600" />
<div className="flex-1">
<label
htmlFor={method.value}
className="text-sm font-medium cursor-pointer"
>
{method.label}
</label>
<p className="text-xs text-gray-500 mt-0.5">
{method.description}
</p>
</div>
{method.badge && (
<span
className={`text-xs text-white px-2 py-1 rounded-full ${method.badgeColor}`}
>
{method.badge}
</span>
)}
</div>
);
})}
</RadioGroup>
<div className="p-3 bg-gray-50 rounded-md">
<p className="text-xs text-gray-600">
💡 결제 수단은 예약 후에도 변경 가능합니다
</p>
</div>
</div>
);
}서비스 패키지 선택 폼
import { RadioGroup, RadioGroupItem } from "@vortex/ui-cals";
import { useState } from "react";
export default function ServicePackageForm() {
const [selectedPackage, setSelectedPackage] = useState("premium");
const packages = [
{
value: "basic",
name: "기본 패키지",
description: "컷 + 샴푸",
duration: 50,
price: 40000,
originalPrice: null,
discount: null,
badge: null,
borderColor: "border-gray-200",
bgColor: "",
},
{
value: "premium",
name: "프리미엄 패키지",
description: "컷 + 펌 + 트리트먼트 + 샴푸",
duration: 150,
price: 180000,
originalPrice: 200000,
discount: "10% 할인",
badge: "인기",
borderColor: "border-[#9c27b0]",
bgColor: "bg-purple-50",
},
{
value: "deluxe",
name: "디럭스 패키지",
description: "컷 + 염색 + 클리닉 + 트리트먼트 + 샴푸",
duration: 180,
price: 280000,
originalPrice: null,
discount: null,
badge: null,
borderColor: "border-gray-200",
bgColor: "",
},
];
return (
<div className="space-y-4 max-w-md">
<h3 className="text-base font-semibold text-gray-900">
서비스 패키지 선택 <span className="text-[#e91e63]">*</span>
</h3>
<RadioGroup value={selectedPackage} onValueChange={setSelectedPackage}>
{packages.map((pkg) => (
<div
key={pkg.value}
className={`
flex items-start space-x-3 p-4 border-2 rounded-lg transition-all cursor-pointer
${pkg.bgColor}
${
selectedPackage === pkg.value
? "border-[#e91e63] ring-2 ring-[#e91e63]/20"
: pkg.borderColor
}
`}
>
<RadioGroupItem
value={pkg.value}
id={pkg.value}
className="mt-1 border-[#e91e63] data-[state=checked]:bg-[#e91e63] data-[state=checked]:border-[#e91e63]"
/>
<div className="flex-1">
<label
htmlFor={pkg.value}
className="text-sm font-medium cursor-pointer"
>
{pkg.name}
</label>
<p className="text-xs text-gray-500 mt-1">{pkg.description}</p>
<p className="text-xs text-gray-400 mt-1">
소요 시간: {pkg.duration}분
</p>
<div className="flex items-center gap-2 mt-2">
<p className="text-base font-semibold text-[#e91e63]">
₩{pkg.price.toLocaleString()}
</p>
{pkg.originalPrice && (
<>
<span className="text-xs text-gray-500 line-through">
₩{pkg.originalPrice.toLocaleString()}
</span>
<span className="text-xs text-[#9c27b0] font-medium">
{pkg.discount}
</span>
</>
)}
</div>
</div>
{pkg.badge && (
<span className="text-xs bg-[#9c27b0] text-white px-2 py-1 rounded-full">
{pkg.badge}
</span>
)}
</div>
))}
</RadioGroup>
<div className="p-4 bg-pink-50 rounded-lg border border-[#e91e63]/20">
<p className="text-sm font-medium text-[#e91e63]">
선택한 패키지:{" "}
{packages.find((p) => p.value === selectedPackage)?.name}
</p>
<p className="text-sm text-gray-700 mt-1">
총 비용: ₩
{packages
.find((p) => p.value === selectedPackage)
?.price.toLocaleString()}
</p>
<p className="text-xs text-gray-500 mt-1">
소요 시간:{" "}
{packages.find((p) => p.value === selectedPackage)?.duration}분
</p>
</div>
</div>
);
}관련 컴포넌트
Last updated on