# UI 컴포넌트 카탈로그

**경로**: `src/components/ui/`
**기반**: shadcn/ui (Radix UI + Tailwind CSS)

---

## 입력 컴포넌트

### 기본 입력

| 컴포넌트 | 파일 | 용도 |
|----------|------|------|
| `Input` | `input.tsx` | 텍스트 입력 |
| `Textarea` | `textarea.tsx` | 여러 줄 텍스트 |
| `Checkbox` | `checkbox.tsx` | 체크박스 |
| `RadioGroup` | `radio-group.tsx` | 라디오 버튼 |
| `Switch` | `switch.tsx` | 토글 스위치 |
| `Slider` | `slider.tsx` | 슬라이더 |
| `Select` | `select.tsx` | 셀렉트 (Radix UI) |

### 특화 입력

| 컴포넌트 | 파일 | 용도 | 특징 |
|----------|------|------|------|
| `DatePicker` | `date-picker.tsx` | 날짜 선택 | 한글 locale, 주말/휴일 색상, 연/월 선택, "오늘" 버튼 |
| `DateRangePicker` | `date-range-picker.tsx` | 기간 선택 | 시작~종료 날짜 |
| `DateTimePicker` | `date-time-picker.tsx` | 날짜+시간 | |
| `TimePicker` | `time-picker.tsx` | 시간만 | |
| `PhoneInput` | `phone-input.tsx` | 전화번호 | 자동 하이픈 (010-1234-5678) |
| `BusinessNumberInput` | `business-number-input.tsx` | 사업자번호 | 자동 포맷 (000-00-00000) |
| `PersonalNumberInput` | `personal-number-input.tsx` | 주민번호 | 마스킹 가능 |
| `CardNumberInput` | `card-number-input.tsx` | 카드번호 | 4자리 구분 |
| `AccountNumberInput` | `account-number-input.tsx` | 계좌번호 | |
| `NumberInput` | `number-input.tsx` | 숫자 | |
| `CurrencyInput` | `currency-input.tsx` | 금액 | 천단위 콤마, ₩ 접두사 |
| `QuantityInput` | `quantity-input.tsx` | 수량 | +/- 버튼 |
| `FileInput` | `file-input.tsx` | 파일 | |
| `FileDropzone` | `file-dropzone.tsx` | 파일 드래그 앤 드롭 | |
| `ImageUpload` | `image-upload.tsx` | 이미지 업로드 | 미리보기 |

### 검색/선택

| 컴포넌트 | 파일 | 용도 |
|----------|------|------|
| `SearchableSelect` | `searchable-select.tsx` | 검색 가능 셀렉트 |
| `MultiSelectCombobox` | `multi-select-combobox.tsx` | 다중 선택 콤보박스 |
| `Command` | `command.tsx` | 검색/필터 커맨드 팔레트 |

---

## 피드백 컴포넌트

| 컴포넌트 | 파일 | 용도 |
|----------|------|------|
| `Button` | `button.tsx` | 버튼 (variant: default, destructive, outline, secondary, ghost, link) |
| `Badge` | `badge.tsx` | 뱃지 |
| `Alert` | `alert.tsx` | 알림 |
| `AlertDialog` | `alert-dialog.tsx` | 알림 다이얼로그 |
| `ConfirmDialog` | `confirm-dialog.tsx` | 확인 다이얼로그 |
| `ErrorCard` | `error-card.tsx` | 에러 카드 |
| `ErrorMessage` | `error-message.tsx` | 에러 메시지 |
| `LoadingSpinner` | `loading-spinner.tsx` | 로딩 스피너 |
| `Skeleton` | `skeleton.tsx` | 스켈레톤 (로딩 플레이스홀더) |
| `toast` | `sonner` 라이브러리 | 토스트 알림 |

---

## 레이아웃 컴포넌트

| 컴포넌트 | 파일 | 용도 |
|----------|------|------|
| `Card` | `card.tsx` | 카드 (CardHeader, CardContent, CardFooter) |
| `Dialog` | `dialog.tsx` | 다이얼로그 (모달) |
| `Drawer` | `drawer.tsx` | 드로어 (하단/측면 패널) |
| `Popover` | `popover.tsx` | 팝오버 |
| `Sheet` | `sheet.tsx` | 시트 (측면 패널) |
| `Accordion` | `accordion.tsx` | 아코디언 (접기/펼치기) |
| `Tabs` | `tabs.tsx` | 탭 |
| `Table` | `table.tsx` | 테이블 (Table, TableHeader, TableBody, TableRow, TableCell) |

---

## 기타 컴포넌트

| 컴포넌트 | 파일 | 용도 |
|----------|------|------|
| `Label` | `label.tsx` | 라벨 |
| `Separator` | `separator.tsx` | 구분선 |
| `ScrollArea` | `scroll-area.tsx` | 스크롤 영역 |
| `Tooltip` | `tooltip.tsx` | 툴팁 |
| `Progress` | `progress.tsx` | 진행률 바 |
| `FileList` | `file-list.tsx` | 파일 목록 표시 |
| `ChartWrapper` | `chart-wrapper.tsx` | 차트 래퍼 (Recharts) |
| `EmptyState` | `empty-state.tsx` | 빈 상태 표시 |

---

## DatePicker 사용법

프로젝트 전체에서 `<input type="date">` 대신 사용.

```tsx
import { DatePicker } from '@/components/ui/date-picker';

// 기본
<DatePicker
  value={date}              // "yyyy-MM-dd" 문자열
  onChange={(date) => setDate(date)}
/>

// 옵션
<DatePicker
  value={date}
  onChange={setDate}
  placeholder="날짜 선택"
  size="sm"                  // "default" | "sm" | "lg"
  disabled={!isEditMode}
  minDate={new Date('2024-01-01')}
  maxDate={new Date()}
/>
```

**Props**:
- `value`: `string` (yyyy-MM-dd 형식)
- `onChange`: `(date: string) => void`
- `size?`: `"default"` | `"sm"` | `"lg"`
- `disabled?`, `placeholder?`, `className?`
- `minDate?`, `maxDate?`: `Date` 타입 (**문자열 아님**)

---

## Radix UI Select 주의사항

빈 값('')으로 마운트 후 value 변경이 반영 안 되는 버그:

```tsx
// ✅ key prop으로 강제 리마운트
<Select
  key={`${fieldKey}-${stringValue}`}
  value={stringValue}
  onValueChange={onChange}
>
  {/* options */}
</Select>
```

---

## 팝업 정책

```
❌ 금지: alert(), confirm(), prompt()
✅ 사용: AlertDialog, ConfirmDialog, toast (sonner)
```

```tsx
// 토스트
import { toast } from 'sonner';
toast.success('저장되었습니다');
toast.error('오류가 발생했습니다');

// 확인 다이얼로그
<AlertDialog open={open} onOpenChange={setOpen}>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>삭제 확인</AlertDialogTitle>
      <AlertDialogDescription>정말 삭제하시겠습니까?</AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>취소</AlertDialogCancel>
      <AlertDialogAction onClick={handleDelete}>삭제</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>
```
