From 388b113b58597cbe9e5367b0440b0e6330b8d06f Mon Sep 17 00:00:00 2001 From: byeongcheolryu Date: Mon, 29 Dec 2025 17:54:27 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A0=95=EA=B4=80=EB=A6=AC=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99=20UI=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20?= =?UTF-8?q?=ED=92=88=EC=A7=88=EC=9D=B8=EC=A0=95=EC=8B=AC=EC=82=AC=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EA=B2=BD=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 자동 분류 규칙 리스트 UI 기획서대로 수정 - 번호, 개별 품목 지정 표시, 배지, 수정/삭제 버튼 - 규칙 수정 기능 추가 (기존 품목 체크 상태 유지) - 개별 품목 모달 UI 기획서대로 재구현 - 검색, 품목유형 필터, 체크박스 테이블 - 품질인정심사시스템 경로 이동 (dev → quality/qms) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../qms}/components/DocumentList.tsx | 0 .../qms}/components/Filters.tsx | 0 .../qms}/components/Header.tsx | 0 .../qms}/components/InspectionModal.tsx | 0 .../qms}/components/ReportList.tsx | 0 .../qms}/components/RouteList.tsx | 0 .../documents/BendingInspectionDocument.tsx | 0 .../documents/ImportInspectionDocument.tsx | 0 .../documents/JointbarInspectionDocument.tsx | 0 .../documents/ProductInspectionDocument.tsx | 0 .../documents/QualityDocumentUploader.tsx | 0 .../documents/ScreenInspectionDocument.tsx | 0 .../documents/SlatInspectionDocument.tsx | 0 .../qms}/components/documents/index.ts | 0 .../qms}/mockData.ts | 0 .../qms}/page.tsx | 0 .../qms}/types.ts | 0 .../process-management/ProcessForm.tsx | 165 +++++-- .../process-management/RuleModal.tsx | 452 ++++++++++++++---- src/components/quotes/index.ts | 6 +- 20 files changed, 473 insertions(+), 150 deletions(-) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/DocumentList.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/Filters.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/Header.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/InspectionModal.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/ReportList.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/RouteList.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/BendingInspectionDocument.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/ImportInspectionDocument.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/JointbarInspectionDocument.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/ProductInspectionDocument.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/QualityDocumentUploader.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/ScreenInspectionDocument.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/SlatInspectionDocument.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/components/documents/index.ts (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/mockData.ts (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/page.tsx (100%) rename src/app/[locale]/(protected)/{dev/quality-inspection => quality/qms}/types.ts (100%) diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/DocumentList.tsx b/src/app/[locale]/(protected)/quality/qms/components/DocumentList.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/DocumentList.tsx rename to src/app/[locale]/(protected)/quality/qms/components/DocumentList.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/Filters.tsx b/src/app/[locale]/(protected)/quality/qms/components/Filters.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/Filters.tsx rename to src/app/[locale]/(protected)/quality/qms/components/Filters.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/Header.tsx b/src/app/[locale]/(protected)/quality/qms/components/Header.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/Header.tsx rename to src/app/[locale]/(protected)/quality/qms/components/Header.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/InspectionModal.tsx b/src/app/[locale]/(protected)/quality/qms/components/InspectionModal.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/InspectionModal.tsx rename to src/app/[locale]/(protected)/quality/qms/components/InspectionModal.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/ReportList.tsx b/src/app/[locale]/(protected)/quality/qms/components/ReportList.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/ReportList.tsx rename to src/app/[locale]/(protected)/quality/qms/components/ReportList.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/RouteList.tsx b/src/app/[locale]/(protected)/quality/qms/components/RouteList.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/RouteList.tsx rename to src/app/[locale]/(protected)/quality/qms/components/RouteList.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/BendingInspectionDocument.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/BendingInspectionDocument.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/BendingInspectionDocument.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/BendingInspectionDocument.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/ImportInspectionDocument.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/ImportInspectionDocument.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/ImportInspectionDocument.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/ImportInspectionDocument.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/JointbarInspectionDocument.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/JointbarInspectionDocument.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/JointbarInspectionDocument.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/JointbarInspectionDocument.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/ProductInspectionDocument.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/ProductInspectionDocument.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/ProductInspectionDocument.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/ProductInspectionDocument.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/QualityDocumentUploader.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/QualityDocumentUploader.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/QualityDocumentUploader.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/QualityDocumentUploader.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/ScreenInspectionDocument.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/ScreenInspectionDocument.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/ScreenInspectionDocument.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/ScreenInspectionDocument.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/SlatInspectionDocument.tsx b/src/app/[locale]/(protected)/quality/qms/components/documents/SlatInspectionDocument.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/SlatInspectionDocument.tsx rename to src/app/[locale]/(protected)/quality/qms/components/documents/SlatInspectionDocument.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/components/documents/index.ts b/src/app/[locale]/(protected)/quality/qms/components/documents/index.ts similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/components/documents/index.ts rename to src/app/[locale]/(protected)/quality/qms/components/documents/index.ts diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/mockData.ts b/src/app/[locale]/(protected)/quality/qms/mockData.ts similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/mockData.ts rename to src/app/[locale]/(protected)/quality/qms/mockData.ts diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/page.tsx b/src/app/[locale]/(protected)/quality/qms/page.tsx similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/page.tsx rename to src/app/[locale]/(protected)/quality/qms/page.tsx diff --git a/src/app/[locale]/(protected)/dev/quality-inspection/types.ts b/src/app/[locale]/(protected)/quality/qms/types.ts similarity index 100% rename from src/app/[locale]/(protected)/dev/quality-inspection/types.ts rename to src/app/[locale]/(protected)/quality/qms/types.ts diff --git a/src/components/process-management/ProcessForm.tsx b/src/components/process-management/ProcessForm.tsx index fba4a0b2..34999c1f 100644 --- a/src/components/process-management/ProcessForm.tsx +++ b/src/components/process-management/ProcessForm.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'; import { useRouter } from 'next/navigation'; -import { X, Save, Plus, Wrench, Trash2, Loader2 } from 'lucide-react'; +import { X, Save, Plus, Wrench, Trash2, Loader2, Pencil } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -74,25 +74,53 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) { // 규칙 모달 상태 const [ruleModalOpen, setRuleModalOpen] = useState(false); + const [editingRule, setEditingRule] = useState(undefined); - // 규칙 추가 - const handleAddRule = useCallback( + // 규칙 추가/수정 + const handleSaveRule = useCallback( (ruleData: Omit) => { - const newRule: ClassificationRule = { - ...ruleData, - id: `rule-${Date.now()}`, - createdAt: new Date().toISOString(), - }; - setClassificationRules((prev) => [...prev, newRule]); + if (editingRule) { + // 수정 모드 + setClassificationRules((prev) => + prev.map((r) => + r.id === editingRule.id + ? { ...r, ...ruleData } + : r + ) + ); + } else { + // 추가 모드 + const newRule: ClassificationRule = { + ...ruleData, + id: `rule-${Date.now()}`, + createdAt: new Date().toISOString(), + }; + setClassificationRules((prev) => [...prev, newRule]); + } + setEditingRule(undefined); }, - [] + [editingRule] ); + // 규칙 수정 모달 열기 + const handleEditRule = useCallback((rule: ClassificationRule) => { + setEditingRule(rule); + setRuleModalOpen(true); + }, []); + // 규칙 삭제 const handleDeleteRule = useCallback((ruleId: string) => { setClassificationRules((prev) => prev.filter((r) => r.id !== ruleId)); }, []); + // 모달 닫기 + const handleModalClose = useCallback((open: boolean) => { + setRuleModalOpen(open); + if (!open) { + setEditingRule(undefined); + } + }, []); + // 제출 const handleSubmit = async () => { if (!processName.trim()) { @@ -273,45 +301,83 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) { ) : (
- {classificationRules.map((rule) => ( -
-
- - {rule.isActive ? '활성' : '비활성'} - -
-
- {rule.ruleType}{' '} - { - MATCHING_TYPE_OPTIONS.find( - (o) => o.value === rule.matchingType - )?.label - }{' '} - "{rule.conditionValue}" -
- {rule.description && ( -
- {rule.description} + {classificationRules.map((rule, index) => { + // 개별 품목인 경우 품목 개수 계산 + const isIndividual = rule.registrationType === 'individual'; + const itemCount = isIndividual + ? rule.conditionValue.split(',').filter(Boolean).length + : 0; + + return ( +
+
+ {/* 번호 */} + + {index + 1}. + +
+ {/* 제목 */} +
+ {isIndividual ? ( + <>개별 품목 지정 - {itemCount}개 품목 + ) : ( + <> + {rule.ruleType}{' '} + { + MATCHING_TYPE_OPTIONS.find( + (o) => o.value === rule.matchingType + )?.label + }{' '} + "{rule.conditionValue}" + + )}
- )} + {/* 뱃지 + 우선순위 */} +
+ + {isIndividual + ? `${itemCount}개 품목 배정됨` + : rule.isActive + ? '활성' + : '비활성'} + + + 우선순위: {rule.priority} + +
+ {/* 설명 */} +
+ {isIndividual + ? `직접 선택한 품목 ${itemCount}개` + : rule.description || ''} +
+
+
+ {/* 수정/삭제 버튼 */} +
+ +
-
- 우선순위: {rule.priority} - -
-
- ))} + ); + })}
)} @@ -381,11 +447,12 @@ export function ProcessForm({ mode, initialData }: ProcessFormProps) {
- {/* 규칙 추가 모달 */} + {/* 규칙 추가/수정 모달 */} ); diff --git a/src/components/process-management/RuleModal.tsx b/src/components/process-management/RuleModal.tsx index 35e6d55d..b522e7cc 100644 --- a/src/components/process-management/RuleModal.tsx +++ b/src/components/process-management/RuleModal.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Dialog, DialogContent, @@ -13,6 +13,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Switch } from '@/components/ui/switch'; +import { Checkbox } from '@/components/ui/checkbox'; import { Select, SelectContent, @@ -20,7 +21,15 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Search } from 'lucide-react'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { Search, Package } from 'lucide-react'; import type { ClassificationRule, RuleRegistrationType, @@ -29,6 +38,29 @@ import type { } from '@/types/process'; import { RULE_TYPE_OPTIONS, MATCHING_TYPE_OPTIONS } from '@/types/process'; +// 품목 유형 옵션 +const ITEM_TYPE_OPTIONS = [ + { value: 'all', label: '전체' }, + { value: '제품', label: '제품' }, + { value: '반제품', label: '반제품' }, + { value: '원자재', label: '원자재' }, + { value: '부자재', label: '부자재' }, +]; + +// Mock 품목 데이터 (추후 API 연동) +const MOCK_ITEMS = [ + { code: '스크린원단-0.3T', name: '스크린원단', type: '부자재' }, + { code: '스크린원단-0.5T', name: '스크린원단', type: '부자재' }, + { code: '슬랫-75mm', name: '슬랫', type: '부자재' }, + { code: '슬랫-100mm', name: '슬랫', type: '부자재' }, + { code: '가이드레일-60×60', name: '가이드레일', type: '부자재' }, + { code: '가이드레일-80×80', name: '가이드레일', type: '부자재' }, + { code: 'SCR-001', name: '스크린A', type: '제품' }, + { code: 'SCR-002', name: '스크린B', type: '제품' }, + { code: 'STEEL-001', name: '철판A', type: '원자재' }, + { code: 'STEEL-002', name: '철판B', type: '원자재' }, +]; + interface RuleModalProps { open: boolean; onOpenChange: (open: boolean) => void; @@ -37,54 +69,150 @@ interface RuleModalProps { } export function RuleModal({ open, onOpenChange, onAdd, editRule }: RuleModalProps) { + // 공통 상태 const [registrationType, setRegistrationType] = useState( editRule?.registrationType || 'pattern' ); + const [description, setDescription] = useState(editRule?.description || ''); + + // 패턴 규칙용 상태 const [ruleType, setRuleType] = useState(editRule?.ruleType || '품목코드'); const [matchingType, setMatchingType] = useState( editRule?.matchingType || 'startsWith' ); const [conditionValue, setConditionValue] = useState(editRule?.conditionValue || ''); const [priority, setPriority] = useState(editRule?.priority || 10); - const [description, setDescription] = useState(editRule?.description || ''); const [isActive, setIsActive] = useState(editRule?.isActive ?? true); - const handleSubmit = () => { - if (!conditionValue.trim()) { - alert('조건 값을 입력해주세요.'); - return; + // 개별 품목용 상태 + const [searchKeyword, setSearchKeyword] = useState(''); + const [selectedItemType, setSelectedItemType] = useState('all'); + const [selectedItemCodes, setSelectedItemCodes] = useState>(new Set()); + + // 필터된 품목 목록 + const filteredItems = MOCK_ITEMS.filter((item) => { + const matchesType = selectedItemType === 'all' || item.type === selectedItemType; + const matchesSearch = + !searchKeyword || + item.code.toLowerCase().includes(searchKeyword.toLowerCase()) || + item.name.toLowerCase().includes(searchKeyword.toLowerCase()); + return matchesType && matchesSearch; + }); + + // 체크박스 토글 + const handleToggleItem = (code: string) => { + setSelectedItemCodes((prev) => { + const newSet = new Set(prev); + if (newSet.has(code)) { + newSet.delete(code); + } else { + newSet.add(code); + } + return newSet; + }); + }; + + // 전체 선택 + const handleSelectAll = () => { + const allCodes = filteredItems.map((item) => item.code); + setSelectedItemCodes(new Set(allCodes)); + }; + + // 초기화 + const handleResetSelection = () => { + setSelectedItemCodes(new Set()); + }; + + // 모달 열릴 때 초기화 또는 수정 데이터 로드 + useEffect(() => { + if (open) { + if (editRule) { + // 수정 모드: 기존 데이터 로드 + setRegistrationType(editRule.registrationType); + setDescription(editRule.description || ''); + setRuleType(editRule.ruleType); + setMatchingType(editRule.matchingType); + setConditionValue(editRule.conditionValue); + setPriority(editRule.priority); + setIsActive(editRule.isActive); + setSearchKeyword(''); + setSelectedItemType('all'); + + // 개별 품목인 경우 선택된 품목 코드 설정 + if (editRule.registrationType === 'individual') { + const codes = editRule.conditionValue.split(',').filter(Boolean); + setSelectedItemCodes(new Set(codes)); + } else { + setSelectedItemCodes(new Set()); + } + } else { + // 추가 모드: 초기화 + setRegistrationType('pattern'); + setDescription(''); + setRuleType('품목코드'); + setMatchingType('startsWith'); + setConditionValue(''); + setPriority(10); + setIsActive(true); + setSearchKeyword(''); + setSelectedItemType('all'); + setSelectedItemCodes(new Set()); + } } + }, [open, editRule]); + + const handleSubmit = () => { + if (registrationType === 'pattern') { + if (!conditionValue.trim()) { + alert('조건 값을 입력해주세요.'); + return; + } + } else { + if (selectedItemCodes.size === 0) { + alert('품목을 최소 1개 이상 선택해주세요.'); + return; + } + } + + // 개별 품목의 경우 conditionValue에 품목코드들을 저장 + const finalConditionValue = + registrationType === 'individual' + ? Array.from(selectedItemCodes).join(',') + : conditionValue.trim(); onAdd({ registrationType, - ruleType, - matchingType, - conditionValue: conditionValue.trim(), - priority, + ruleType: registrationType === 'individual' ? '품목코드' : ruleType, + matchingType: registrationType === 'individual' ? 'equals' : matchingType, + conditionValue: finalConditionValue, + priority: registrationType === 'individual' ? 10 : priority, description: description.trim() || undefined, - isActive, + isActive: registrationType === 'individual' ? true : isActive, }); // Reset form setRegistrationType('pattern'); + setDescription(''); setRuleType('품목코드'); setMatchingType('startsWith'); setConditionValue(''); setPriority(10); - setDescription(''); setIsActive(true); + setSearchKeyword(''); + setSelectedItemType('all'); + setSelectedItemCodes(new Set()); onOpenChange(false); }; return ( - + - 규칙 추가 + {editRule ? '규칙 수정' : '규칙 추가'} -
+
{/* 등록 방식 */}
@@ -107,100 +235,228 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule }: RuleModalProp
- {/* 규칙 유형 */} -
- - -
+ {/* 패턴 규칙 UI */} + {registrationType === 'pattern' && ( + <> + {/* 규칙 유형 */} +
+ + +
- {/* 매칭 방식 */} -
- - -
+ {/* 매칭 방식 */} +
+ + +
- {/* 조건 값 */} -
- -
- setConditionValue(e.target.value)} - placeholder="예: SCR-, E-, STEEL-" - className="flex-1" - /> - -
-

- Enter 키를 누르거나 검색 버튼을 클릭하세요 -

-
+ {/* 조건 값 */} +
+ +
+ setConditionValue(e.target.value)} + placeholder="예: SCR-, E-, STEEL-" + className="flex-1" + /> + +
+

+ Enter 키를 누르거나 검색 버튼을 클릭하세요 +

+
- {/* 우선순위 */} -
- - setPriority(Number(e.target.value))} - min={1} - max={100} - className="w-24" - /> -

낮을수록 먼저 적용됩니다

-
+ {/* 우선순위 - 패턴 규칙에서만 표시 */} +
+ + setPriority(Number(e.target.value))} + min={1} + max={100} + className="w-24" + /> +

낮을수록 먼저 적용됩니다

+
- {/* 설명 */} -
- - setDescription(e.target.value)} - placeholder="규칙에 대한 설명" - /> -
+ {/* 설명 - 패턴 규칙 */} +
+ + setDescription(e.target.value)} + placeholder="규칙에 대한 설명" + /> +
- {/* 활성 상태 */} -
- - -
+ {/* 활성 상태 - 패턴 규칙에서만 표시 */} +
+ + +
+ + )} + + {/* 개별 품목 UI - 기획서 기준 */} + {registrationType === 'individual' && ( + <> + {/* 설명 (선택) */} +
+ + setDescription(e.target.value)} + placeholder="이 품목 그룹에 대한 설명" + /> +
+ + {/* 품목 검색 + 품목 유형 필터 */} +
+
+ +
+ + setSearchKeyword(e.target.value)} + placeholder="품목코드 또는 품목명으로 검색..." + className="pl-9" + /> +
+
+
+ + +
+
+ + {/* 품목 목록 헤더 */} +
+
+ + + 품목 목록 ({filteredItems.length}개) | 선택됨 ({selectedItemCodes.size}개) + +
+
+ + | + +
+
+ + {/* 품목 테이블 */} +
+ + + + + 품목코드 + 품목명 + 품목유형 + + + + {filteredItems.map((item) => ( + handleToggleItem(item.code)} + > + + handleToggleItem(item.code)} + onClick={(e) => e.stopPropagation()} + /> + + {item.code} + {item.name} + {item.type} + + ))} + {filteredItems.length === 0 && ( + + + 검색 결과가 없습니다 + + + )} + +
+
+ + {/* 안내 문구 */} +

+ 이 공정에 배정할 품목을 선택하세요. 다른 공정에 이미 배정된 품목은 표시되지 않습니다. +

+ + )}
- +
); -} \ No newline at end of file +} diff --git a/src/components/quotes/index.ts b/src/components/quotes/index.ts index 7aa0850d..aa759aea 100644 --- a/src/components/quotes/index.ts +++ b/src/components/quotes/index.ts @@ -7,9 +7,9 @@ export { QuoteManagementClient } from './QuoteManagementClient'; // 기존 컴포넌트 export { default as QuoteDocument } from './QuoteDocument'; -export { default as QuoteRegistration } from './QuoteRegistration'; -export { default as QuoteCalculationReport } from './QuoteCalculationReport'; -export { default as PurchaseOrderDocument } from './PurchaseOrderDocument'; +export { QuoteRegistration, INITIAL_QUOTE_FORM } from './QuoteRegistration'; +export { QuoteCalculationReport } from './QuoteCalculationReport'; +export { PurchaseOrderDocument } from './PurchaseOrderDocument'; // 타입 export type {