diff options
Diffstat (limited to 'components/common/vendor/README.md')
| -rw-r--r-- | components/common/vendor/README.md | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/components/common/vendor/README.md b/components/common/vendor/README.md new file mode 100644 index 00000000..7c9e54d9 --- /dev/null +++ b/components/common/vendor/README.md @@ -0,0 +1,211 @@ +# 벤더 선택기 (Vendor Selector) + +벤더 선택을 위한 공용 컴포넌트입니다. 다양한 형태로 벤더를 검색하고 선택할 수 있는 기능을 제공합니다. + +## 기능 + +- PostgreSQL DB에서 벤더 실시간 검색 및 선택 +- 벤더명, 벤더코드로 필터링 가능 +- 상태별 필터링 지원 (ACTIVE, PENDING_REVIEW, APPROVED, INACTIVE 등) +- 페이지네이션 지원 +- 단일/다중 선택 모드 +- 최대 선택 개수 제한 +- 제외 벤더 설정 가능 +- 반응형 다이얼로그 UI + +## 컴포넌트 구조 + +### 1. VendorSelector (기본 선택기) + +Popover 기반의 기본 벤더 선택기 컴포넌트 + +### 2. VendorSelectorDialogSingle (단일 선택 다이얼로그) + +Dialog 형태의 단일 벤더 선택 컴포넌트 + +### 3. VendorSelectorDialogMulti (다중 선택 다이얼로그) + +Dialog 형태의 다중 벤더 선택 컴포넌트 + +## 사용법 + +### 기본 선택기 사용법 + +```tsx +import { VendorSelector, VendorSearchItem } from '@/components/common/vendor' + +function MyComponent() { + const [selectedVendors, setSelectedVendors] = useState<VendorSearchItem[]>([]) + + return ( + <VendorSelector + selectedVendors={selectedVendors} + onVendorsChange={(vendors) => { + console.log('선택된 벤더들:', vendors) + setSelectedVendors(vendors) + }} + placeholder="벤더를 검색하세요..." + statusFilter="ACTIVE" // ACTIVE 상태의 벤더만 표시 + /> + ) +} +``` + +### 단일 선택 다이얼로그 사용법 + +```tsx +import { VendorSelectorDialogSingle, VendorSearchItem } from '@/components/common/vendor' + +function MyComponent() { + const [selectedVendor, setSelectedVendor] = useState<VendorSearchItem | null>(null) + + return ( + <VendorSelectorDialogSingle + triggerLabel="벤더 선택" + selectedVendor={selectedVendor} + onVendorSelect={(vendor) => { + console.log('선택된 벤더:', vendor) + setSelectedVendor(vendor) + }} + title="협력업체 선택" + description="프로젝트에 참여할 협력업체를 선택해주세요." + /> + ) +} +``` + +### 다중 선택 다이얼로그 사용법 + +```tsx +import { VendorSelectorDialogMulti, VendorSearchItem } from '@/components/common/vendor' + +function MyComponent() { + const [selectedVendors, setSelectedVendors] = useState<VendorSearchItem[]>([]) + + return ( + <VendorSelectorDialogMulti + triggerLabel="벤더 선택 (다중)" + selectedVendors={selectedVendors} + onVendorsSelect={(vendors) => { + console.log('선택된 벤더들:', vendors) + setSelectedVendors(vendors) + }} + maxSelections={5} + title="협력업체 선택" + description="프로젝트에 참여할 협력업체들을 선택해주세요. (최대 5개)" + showSelectedInTrigger={true} + /> + ) +} +``` + +## Props + +### VendorSelector Props + +| Prop | 타입 | 필수 | 기본값 | 설명 | +|------|------|------|--------|------| +| `selectedVendors` | `VendorSearchItem[]` | ❌ | `[]` | 선택된 벤더들 | +| `onVendorsChange` | `(vendors: VendorSearchItem[]) => void` | ❌ | - | 벤더 선택 변경 시 호출되는 콜백 | +| `singleSelect` | `boolean` | ❌ | `false` | 단일 선택 모드 여부 | +| `placeholder` | `string` | ❌ | `"벤더를 검색하세요..."` | 검색 입력창 placeholder | +| `noValuePlaceHolder` | `string` | ❌ | `"벤더를 검색해주세요"` | 선택된 벤더가 없을 때 표시되는 텍스트 | +| `disabled` | `boolean` | ❌ | `false` | 비활성화 여부 | +| `className` | `string` | ❌ | - | 추가 CSS 클래스 | +| `closeOnSelect` | `boolean` | ❌ | `true` | 선택 후 자동 닫기 여부 | +| `excludeVendorIds` | `Set<number>` | ❌ | - | 제외할 벤더 ID들 | +| `showInitialData` | `boolean` | ❌ | `true` | 초기 데이터 표시 여부 | +| `maxSelections` | `number` | ❌ | - | 최대 선택 가능한 벤더 개수 | +| `statusFilter` | `string` | ❌ | - | 벤더 상태 필터 | + +### VendorSelectorDialogSingle Props + +| Prop | 타입 | 필수 | 기본값 | 설명 | +|------|------|------|--------|------| +| `triggerLabel` | `string` | ❌ | `"벤더 선택"` | 트리거 버튼 텍스트 | +| `selectedVendor` | `VendorSearchItem \| null` | ❌ | `null` | 선택된 벤더 | +| `onVendorSelect` | `(vendor: VendorSearchItem \| null) => void` | ❌ | - | 벤더 선택 완료 시 호출되는 콜백 | +| `placeholder` | `string` | ❌ | `"벤더를 검색하세요..."` | 검색 입력창 placeholder | +| `title` | `string` | ❌ | `"벤더 선택"` | Dialog 제목 | +| `description` | `string` | ❌ | `"원하는 벤더를 검색하고 선택해주세요."` | Dialog 설명 | +| `disabled` | `boolean` | ❌ | `false` | 트리거 버튼 비활성화 여부 | +| `triggerVariant` | `ButtonVariant` | ❌ | `"outline"` | 트리거 버튼 variant | +| `excludeVendorIds` | `Set<number>` | ❌ | - | 제외할 벤더 ID들 | +| `showInitialData` | `boolean` | ❌ | `true` | 초기 데이터 표시 여부 | +| `statusFilter` | `string` | ❌ | - | 벤더 상태 필터 | + +### VendorSelectorDialogMulti Props + +VendorSelectorDialogSingle과 유사하지만 다음이 추가됩니다: + +| Prop | 타입 | 필수 | 기본값 | 설명 | +|------|------|------|--------|------| +| `selectedVendors` | `VendorSearchItem[]` | ❌ | `[]` | 선택된 벤더들 | +| `onVendorsSelect` | `(vendors: VendorSearchItem[]) => void` | ❌ | - | 벤더 선택 완료 시 호출되는 콜백 | +| `maxSelections` | `number` | ❌ | - | 최대 선택 가능한 벤더 개수 | +| `showSelectedInTrigger` | `boolean` | ❌ | `true` | 트리거 버튼에서 선택된 벤더들을 표시할지 여부 | + +## 타입 + +### VendorSearchItem + +```tsx +interface VendorSearchItem { + id: number // 벤더 ID + vendorName: string // 벤더명 + vendorCode: string | null // 벤더코드 (없을 수 있음) + status: string // 벤더 상태 + displayText: string // 표시용 텍스트 (vendorName + vendorCode) +} +``` + +### VendorSearchOptions + +```tsx +interface VendorSearchOptions { + searchTerm?: string // 검색어 + statusFilter?: string // 상태 필터 + limit?: number // 조회 제한 수 + offset?: number // 조회 시작 위치 + sortBy?: 'vendorName' | 'vendorCode' | 'status' // 정렬 기준 + sortOrder?: 'asc' | 'desc' // 정렬 순서 +} +``` + +### VendorPagination + +```tsx +interface VendorPagination { + page: number // 현재 페이지 + perPage: number // 페이지당 항목 수 + total: number // 전체 항목 수 + pageCount: number // 전체 페이지 수 + hasNextPage: boolean // 다음 페이지 존재 여부 + hasPrevPage: boolean // 이전 페이지 존재 여부 +} +``` + +## 데이터 소스 + +**PostgreSQL DB의 `vendors` 테이블** + +- **필드**: + - id (벤더 ID) + - vendorName (벤더명) + - vendorCode (벤더코드) + - status (벤더 상태) + +## 동작 방식 + +1. **서버 액션 호출**: Next.js 서버 액션을 통한 안전한 데이터 페칭 +2. **PostgreSQL 조회**: Drizzle ORM을 사용하여 vendors 테이블에서 조회 +3. **검색 최적화**: ILIKE를 사용한 대소문자 무관 검색 +4. **페이지네이션**: 대량 데이터 처리를 위한 페이지네이션 지원 +5. **상태 필터링**: 벤더 상태별 필터링 지원 + +## 주의사항 + +- PostgreSQL DB 연결이 필요합니다 +- vendors 스키마의 vendors 테이블에 데이터가 있어야 합니다 +- 검색은 벤더명과 벤더코드에 대해서만 수행됩니다 +- 상태 값은 테이블에 정의된 값을 사용해야 합니다 |
