'use client'; import { useState, useEffect, useCallback } from 'react'; import { getItemList, getItemTypeOptions, type ItemOption } from './actions'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Checkbox } from '@/components/ui/checkbox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Search } from 'lucide-react'; import type { ClassificationRule } from '@/types/process'; import { PROCESS_CATEGORY_OPTIONS } from '@/types/process'; import { toast } from 'sonner'; // 공정 필터 옵션 const PROCESS_FILTER_OPTIONS = [ { value: 'all', label: '전체' }, { value: '스크린', label: '스크린' }, { value: '슬릿', label: '슬릿' }, { value: '절곡', label: '절곡' }, ]; // 공정 필터에 따른 구분 필터 옵션 function getCategoryFilterOptions(processFilter: string): { value: string; label: string }[] { if (processFilter === 'all') { return [{ value: 'all', label: '전체' }]; } const categories = PROCESS_CATEGORY_OPTIONS[processFilter]; if (!categories || categories.length === 0) { return [{ value: 'all', label: '전체' }]; } // 스크린의 경우 '없음'만 있으므로 전체만 표시 if (categories.length === 1 && categories[0].value === '없음') { return [{ value: 'all', label: '전체' }]; } return [{ value: 'all', label: '전체' }, ...categories]; } interface RuleModalProps { open: boolean; onOpenChange: (open: boolean) => void; onAdd: (rule: Omit) => void; editRule?: ClassificationRule; /** 현재 공정 ID (다른 공정에 이미 배정된 품목 제외용) */ processId?: string; /** 현재 공정명 (하단 안내 문구용) */ processName?: string; /** 이미 등록된 품목 ID 목록 (검색 결과에서 제외) */ registeredItemIds?: Set; } export function RuleModal({ open, onOpenChange, onAdd, editRule, processId, processName, registeredItemIds }: RuleModalProps) { // 검색/필터 상태 const [searchKeyword, setSearchKeyword] = useState(''); const [selectedItemType, setSelectedItemType] = useState('all'); const [selectedItemIds, setSelectedItemIds] = useState>(new Set()); // 공정/구분 필터 상태 const [processFilter, setProcessFilter] = useState('all'); const [categoryFilter, setCategoryFilter] = useState('all'); // 품목 목록 API 상태 const [itemList, setItemList] = useState([]); const [isItemsLoading, setIsItemsLoading] = useState(false); // 품목 유형 옵션 (common_codes에서 동적 조회) const [itemTypeOptions, setItemTypeOptions] = useState>([ { value: 'all', label: '전체' }, ]); // 구분 필터 옵션 (공정 필터에 따라 변경) const categoryFilterOptions = getCategoryFilterOptions(processFilter); // 품목 목록 로드 const loadItems = useCallback(async (q?: string, itemType?: string) => { setIsItemsLoading(true); const items = await getItemList({ q: q || undefined, itemType: itemType === 'all' ? undefined : itemType, size: 1000, excludeProcessId: processId, }); // 이미 등록된 품목 필터링 const filtered = registeredItemIds && registeredItemIds.size > 0 ? items.filter((item) => !registeredItemIds.has(item.id)) : items; setItemList(filtered); setIsItemsLoading(false); }, [processId, registeredItemIds]); // 검색어 유효성 검사 함수 const isValidSearchKeyword = (keyword: string): boolean => { if (!keyword || keyword.trim() === '') return false; const trimmed = keyword.trim(); const hasKorean = /[가-힣]/.test(trimmed); if (hasKorean) return trimmed.length >= 1; return trimmed.length >= 2; }; // 검색어 변경 시 API 호출 (debounce) useEffect(() => { if (!isValidSearchKeyword(searchKeyword)) { setItemList([]); return; } const timer = setTimeout(() => { loadItems(searchKeyword, selectedItemType); }, 300); return () => clearTimeout(timer); }, [searchKeyword, selectedItemType, loadItems]); // 모달 열릴 때 초기화 useEffect(() => { if (open) { // 품목유형 옵션 로드 getItemTypeOptions().then((options) => { setItemTypeOptions([{ value: 'all', label: '전체' }, ...options]); }); if (editRule) { // 수정 모드: 기존 선택된 품목 ID 설정 if (editRule.registrationType === 'individual') { const ids = editRule.conditionValue.split(',').filter(Boolean); setSelectedItemIds(new Set(ids)); } else { setSelectedItemIds(new Set()); } } else { setSelectedItemIds(new Set()); } setSearchKeyword(''); setSelectedItemType('all'); setProcessFilter('all'); setCategoryFilter('all'); setItemList([]); } }, [open, editRule]); // 공정 필터 변경 시 구분 필터 리셋 useEffect(() => { setCategoryFilter('all'); }, [processFilter]); // 체크박스 토글 const handleToggleItem = (id: string) => { setSelectedItemIds((prev) => { const newSet = new Set(prev); if (newSet.has(id)) { newSet.delete(id); } else { newSet.add(id); } return newSet; }); }; // 저장 const handleSubmit = () => { if (selectedItemIds.size === 0) { toast.warning('품목을 최소 1개 이상 선택해주세요.'); return; } const finalConditionValue = Array.from(selectedItemIds).join(','); onAdd({ registrationType: 'individual', ruleType: '품목코드', matchingType: 'equals', conditionValue: finalConditionValue, priority: 10, description: undefined, isActive: true, }); // Reset setSearchKeyword(''); setSelectedItemType('all'); setSelectedItemIds(new Set()); setProcessFilter('all'); setCategoryFilter('all'); onOpenChange(false); }; return ( 공정 품목 선택
{/* 검색 입력 */}
setSearchKeyword(e.target.value)} placeholder="품목코드 또는 품목명으로 검색..." className="pl-9" />
{/* 카운트 + 필터 행 */}
{isItemsLoading ? ( '로딩 중...' ) : ( <> 총 {itemList.length}건{' '} {selectedItemIds.size > 0 && ( {selectedItemIds.size}건 선택됨 )} )}
{/* 품목 테이블 */}
품목유형 품목코드 품목명 {/* TODO: API에서 process_name, process_category 필드 지원 후 실제 데이터 표시 */} 공정 구분 {isItemsLoading ? ( 품목 목록을 불러오는 중... ) : itemList.length === 0 ? ( {searchKeyword.trim() === '' ? '품목을 검색해주세요 (한글 1자 이상, 영문 2자 이상)' : '검색 결과가 없습니다'} ) : ( itemList.map((item) => ( handleToggleItem(item.id)} > handleToggleItem(item.id)} onClick={(e) => e.stopPropagation()} /> {item.type} {item.code} {item.fullName} {/* TODO: API 지원 후 item.processName / item.processCategory 표시 */} - - )) )}
{/* 안내 문구 */}

선택 후 저장하시면 선택한 품목들이{' '} {processName || '해당'} {' '} 공정으로 변경됩니다.

); }