Badge
상태나 카테고리를 표시하는 뱃지 컴포넌트입니다.
개요
Badge는 작은 라벨 형태로 상태, 카테고리, 또는 개수를 표시하는 컴포넌트입니다. Cals Badge는 Cals 브랜드 컬러(Pink, Blue, Purple)와 예약 상태 컬러 5가지를 활용하여 예약 시스템에서 직관적으로 정보를 전달합니다.
주요 특징:
- 5가지 기본 Variant + 5가지 예약 상태 Variant
- 3가지 Size (sm, md, lg)
- Cals 브랜드 컬러 적용
- 예약 상태 컬러 완벽 지원 (Available, Pending, Confirmed, Cancelled, Completed)
- 닫기 버튼 옵션
- 숫자 뱃지 지원
사용 사례:
- Cals 예약 상태 표시 (예약 가능, 승인 대기, 확정, 취소, 완료)
- 예약 목록/캘린더의 상태 뱃지
- 알림 개수 표시
- 예약 카테고리 태그
설치
npx @vortex/cli add badge --package cals기본 사용법
import "@vortex/ui-cals/theme";
import { Badge } from "@vortex/ui-cals";
export default function Example() {
return <Badge>New</Badge>;
}Variants (변형)
Default
<Badge variant="default">Default</Badge>Success
<Badge variant="success">완료</Badge>Warning
<Badge variant="warning">주의 필요</Badge>Danger
<Badge variant="danger">오류</Badge>Info
<Badge variant="info">정보</Badge>예약 상태 Variants ⭐
Available (예약 가능)
예약 가능한 상태를 나타내는 Green 뱃지입니다.
<Badge variant="available">예약 가능</Badge>사용 예시:
- 예약 가능한 시간대 표시
- 예약 가능 좌석/테이블 표시
- 예약 캘린더의 가능 날짜
Pending (승인 대기)
승인 대기 상태를 나타내는 Orange 뱃지입니다.
<Badge variant="pending">승인 대기</Badge>사용 예시:
- 관리자 승인 대기 예약
- 결제 대기 예약
- 확인 대기 상태
Confirmed (예약 확정)
예약 확정 상태를 나타내는 Blue 뱃지입니다.
<Badge variant="confirmed">예약 확정</Badge>사용 예시:
- 확정된 예약 표시
- 확인 완료된 예약
- 진행 예정 예약
Cancelled (예약 취소)
예약 취소 상태를 나타내는 Red 뱃지입니다.
<Badge variant="cancelled">예약 취소</Badge>사용 예시:
- 취소된 예약 표시
- 노쇼(No-show) 표시
- 환불 완료 표시
Completed (서비스 완료)
서비스 완료 상태를 나타내는 Purple 뱃지입니다.
<Badge variant="completed">서비스 완료</Badge>사용 예시:
- 완료된 예약 표시
- 서비스 제공 완료
- 리뷰 대기 상태
Sizes (크기)
Small
<Badge size="sm">Small</Badge>Medium (기본)
<Badge size="md">Medium</Badge>Large
<Badge size="lg">Large</Badge>Cals 브랜딩
브랜드 컬러
Cals Badge는 다음 브랜드 컬러를 사용합니다:
- Primary (Pink):
#e91e63- 주요 카테고리, 강조 - Secondary (Blue):
#03a9f4- 보조 정보 - Accent (Purple):
#9c27b0- 특별 상태
예약 상태 컬러 ⭐
Cals는 예약 시스템에 특화된 5가지 상태 컬러를 제공합니다:
- Available (Green):
#4caf50- 예약 가능 - Pending (Orange):
#ff9800- 승인 대기 - Confirmed (Blue):
#03a9f4- 예약 확정 - Cancelled (Red):
#f44336- 예약 취소 - Completed (Purple):
#9c27b0- 서비스 완료
Cals 특화 사용 가이드
예약 상태 표시:
<Badge variant="available">예약 가능</Badge>
<Badge variant="pending">승인 대기</Badge>
<Badge variant="confirmed">예약 확정</Badge>
<Badge variant="cancelled">예약 취소</Badge>
<Badge variant="completed">서비스 완료</Badge>예약 목록에서 사용:
<div className="flex gap-sm">
<Badge variant="available">10:00 예약 가능</Badge>
<Badge variant="pending">11:00 승인 대기</Badge>
<Badge variant="confirmed">14:00 확정</Badge>
<Badge variant="completed">16:00 완료</Badge>
</div>Foundation/iCignal/Cals 비교
| 속성 | Foundation | iCignal | Cals |
|---|---|---|---|
| Primary Color | 중립 블루 | #2196f3 (iCignal Blue) | #e91e63 (Cals Pink) |
| 브랜드 적용 | 없음 | iCignal Analytics | Cals 예약 시스템 |
| 특화 Variants | 없음 | 없음 | 5가지 예약 상태 |
| 사용 맥락 | 범용 | 데이터 상태 표시 | 예약 상태 표시 |
| 예약 상태 지원 | ❌ | ❌ | ✅ Available/Pending/Confirmed/Cancelled/Completed |
| 테마 | 중립적 테마 | iCignal 테마 | Cals 테마 (@vortex/ui-cals/theme) |
import 경로 차이:
// Foundation
import { Badge } from "@vortex/ui-foundation";
// iCignal
import "@vortex/ui-icignal/theme";
import { Badge } from "@vortex/ui-icignal";
// Cals
import "@vortex/ui-cals/theme"; // 테마 import 필수
import { Badge } from "@vortex/ui-cals";Props API
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ’default’ | ‘success’ | ‘warning’ | ‘danger’ | ‘info’ | ‘available’ | ‘pending’ | ‘confirmed’ | ‘cancelled’ | ‘completed' | 'default’ | 뱃지 변형 |
| size | ’sm’ | ‘md’ | ‘lg' | 'md’ | 뱃지 크기 |
| closable | boolean | false | 닫기 버튼 표시 |
| onClose | () => void | - | 닫기 버튼 클릭 핸들러 |
| className | string | - | 추가 CSS 클래스 |
| children | ReactNode | - | 뱃지 내용 |
접근성
Badge 컴포넌트는 WCAG 2.1 AA 기준을 준수합니다.
ARIA 속성:
role="status"(상태 뱃지)aria-label(닫기 버튼)
색상 대비 (Cals 브랜드 컬러 기준):
- Available: 4.5:1 이상
- Pending: 4.5:1 이상
- Confirmed: 4.5:1 이상
- Cancelled: 4.5:1 이상
- Completed: 4.5:1 이상
예제
예제 1: 예약 목록의 상태 표시 ⭐
import "@vortex/ui-cals/theme";
import { Badge } from "@vortex/ui-cals";
import { Calendar, Clock, Check, X, CheckCircle } from "lucide-react";
export default function ReservationList() {
const reservations = [
{
id: 1,
customer: "홍길동",
date: "2024-01-20",
time: "10:00",
status: "available",
statusText: "예약 가능",
},
{
id: 2,
customer: "김철수",
date: "2024-01-20",
time: "11:00",
status: "pending",
statusText: "승인 대기",
},
{
id: 3,
customer: "이영희",
date: "2024-01-20",
time: "14:00",
status: "confirmed",
statusText: "예약 확정",
},
{
id: 4,
customer: "박민수",
date: "2024-01-19",
time: "15:00",
status: "cancelled",
statusText: "예약 취소",
},
{
id: 5,
customer: "정수진",
date: "2024-01-19",
time: "16:00",
status: "completed",
statusText: "서비스 완료",
},
];
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>예약 번호</TableHead>
<TableHead>고객명</TableHead>
<TableHead>예약일시</TableHead>
<TableHead>상태</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{reservations.map((reservation) => (
<TableRow key={reservation.id}>
<TableCell>#{reservation.id}</TableCell>
<TableCell>{reservation.customer}</TableCell>
<TableCell>
{reservation.date} {reservation.time}
</TableCell>
<TableCell>
<Badge variant={reservation.status}>
{reservation.status === "available" && (
<Calendar size={14} className="mr-xs" />
)}
{reservation.status === "pending" && (
<Clock size={14} className="mr-xs" />
)}
{reservation.status === "confirmed" && (
<Check size={14} className="mr-xs" />
)}
{reservation.status === "cancelled" && (
<X size={14} className="mr-xs" />
)}
{reservation.status === "completed" && (
<CheckCircle size={14} className="mr-xs" />
)}
{reservation.statusText}
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}예제 2: 예약 캘린더의 날짜 뱃지 ⭐
import "@vortex/ui-cals/theme";
import { Badge } from "@vortex/ui-cals";
export default function ReservationCalendar() {
const dates = [
{ date: 15, status: "available", count: 5 },
{ date: 16, status: "pending", count: 2 },
{ date: 17, status: "confirmed", count: 8 },
{ date: 18, status: "cancelled", count: 1 },
{ date: 19, status: "completed", count: 10 },
{ date: 20, status: "available", count: 3 },
];
return (
<div className="grid grid-cols-7 gap-md">
{dates.map((day) => (
<div
key={day.date}
className="border rounded-lg p-md hover:shadow-md transition-shadow"
>
<div className="text-center mb-sm">
<span className="text-2xl font-bold">{day.date}</span>
</div>
<Badge
variant={day.status}
size="sm"
className="w-full justify-center"
>
{day.status === "available" && `${day.count}석`}
{day.status === "pending" && `대기 ${day.count}`}
{day.status === "confirmed" && `확정 ${day.count}`}
{day.status === "cancelled" && `취소 ${day.count}`}
{day.status === "completed" && `완료 ${day.count}`}
</Badge>
</div>
))}
</div>
);
}예제 3: 예약 상태별 필터링 ⭐
import "@vortex/ui-cals/theme";
import { Badge } from "@vortex/ui-cals";
import { useState } from "react";
export default function ReservationFilter() {
const [activeFilter, setActiveFilter] = useState("all");
const filters = [
{ id: "all", label: "전체", count: 48 },
{ id: "available", label: "예약 가능", count: 12, variant: "available" },
{ id: "pending", label: "승인 대기", count: 5, variant: "pending" },
{ id: "confirmed", label: "예약 확정", count: 18, variant: "confirmed" },
{ id: "cancelled", label: "예약 취소", count: 3, variant: "cancelled" },
{ id: "completed", label: "서비스 완료", count: 10, variant: "completed" },
];
return (
<div className="space-y-lg">
<div className="flex gap-sm flex-wrap">
{filters.map((filter) => (
<button
key={filter.id}
onClick={() => setActiveFilter(filter.id)}
className={`
px-md py-sm rounded-lg border-2 transition-all
${
activeFilter === filter.id
? "border-primary bg-primary/10"
: "border-border hover:border-primary/50"
}
`}
>
<div className="flex items-center gap-sm">
<span className="font-medium">{filter.label}</span>
<Badge variant={filter.variant || "default"} size="sm">
{filter.count}
</Badge>
</div>
</button>
))}
</div>
<div className="text-sm text-muted-foreground">
{activeFilter === "all" && "전체 예약을 표시합니다"}
{activeFilter === "available" && "예약 가능한 시간대만 표시합니다"}
{activeFilter === "pending" && "승인 대기 중인 예약만 표시합니다"}
{activeFilter === "confirmed" && "확정된 예약만 표시합니다"}
{activeFilter === "cancelled" && "취소된 예약만 표시합니다"}
{activeFilter === "completed" && "완료된 예약만 표시합니다"}
</div>
</div>
);
}예제 4: 알림 개수 뱃지
import "@vortex/ui-cals/theme";
import { Badge } from "@vortex/ui-cals";
import { Bell, Calendar, Clock, Check } from "lucide-react";
export default function NotificationBadges() {
return (
<div className="flex gap-lg">
{/* 전체 알림 */}
<Button variant="ghost" className="relative">
<Bell size={20} />
<Badge variant="danger" size="sm" className="absolute -top-1 -right-1">
8
</Badge>
</Button>
{/* 신규 예약 알림 */}
<Button variant="ghost" className="relative">
<Calendar size={20} />
<Badge
variant="available"
size="sm"
className="absolute -top-1 -right-1"
>
3
</Badge>
</Button>
{/* 승인 대기 알림 */}
<Button variant="ghost" className="relative">
<Clock size={20} />
<Badge variant="pending" size="sm" className="absolute -top-1 -right-1">
5
</Badge>
</Button>
{/* 확정 완료 알림 */}
<Button variant="ghost" className="relative">
<Check size={20} />
<Badge
variant="confirmed"
size="sm"
className="absolute -top-1 -right-1"
>
12
</Badge>
</Button>
</div>
);
}예제 5: 예약 상세 정보 카드
import "@vortex/ui-cals/theme";
import { Badge } from "@vortex/ui-cals";
import { Calendar, Clock, User, MapPin, Phone } from "lucide-react";
export default function ReservationDetailCard() {
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<h3>예약 상세 정보</h3>
<Badge variant="confirmed" size="lg">
<Check size={16} className="mr-xs" />
예약 확정
</Badge>
</div>
</CardHeader>
<CardContent className="space-y-md">
<div className="flex items-center gap-sm">
<Calendar size={16} className="text-muted-foreground" />
<span>2024년 1월 20일 (토)</span>
<Badge variant="available" size="sm">
주말
</Badge>
</div>
<div className="flex items-center gap-sm">
<Clock size={16} className="text-muted-foreground" />
<span>14:00 - 16:00</span>
<Badge variant="pending" size="sm">
2시간
</Badge>
</div>
<div className="flex items-center gap-sm">
<User size={16} className="text-muted-foreground" />
<span>홍길동 (성인 2명, 아동 1명)</span>
</div>
<div className="flex items-center gap-sm">
<MapPin size={16} className="text-muted-foreground" />
<span>VIP룸 A</span>
<Badge variant="accent" size="sm">
프리미엄
</Badge>
</div>
<div className="flex items-center gap-sm">
<Phone size={16} className="text-muted-foreground" />
<span>010-1234-5678</span>
</div>
</CardContent>
<CardFooter>
<div className="w-full p-md bg-muted rounded-md">
<div className="flex items-center justify-between">
<span className="font-medium">예약 금액</span>
<Badge variant="completed" size="lg">
결제 완료 ₩150,000
</Badge>
</div>
</div>
</CardFooter>
</Card>
);
}관련 컴포넌트
- Foundation Badge - 기본 버전 참조
- iCignal Badge - iCignal 버전 참조
- Button - 예약 액션 버튼
- Table - 예약 목록 테이블
- Card - 예약 정보 카드
Last updated on