diff --git a/src/components/process-management/RuleModal.tsx b/src/components/process-management/RuleModal.tsx
index 9a43793c..1cacbe48 100644
--- a/src/components/process-management/RuleModal.tsx
+++ b/src/components/process-management/RuleModal.tsx
@@ -100,13 +100,9 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule, processId, proc
size: 1000,
excludeProcessId: processId,
});
- // 이미 등록된 품목 필터링
- const filtered = registeredItemIds && registeredItemIds.size > 0
- ? items.filter((item) => !registeredItemIds.has(item.id))
- : items;
- setItemList(filtered);
+ setItemList(items);
setIsItemsLoading(false);
- }, [processId, registeredItemIds]);
+ }, [processId]);
// 검색어 유효성 검사 함수
const isValidSearchKeyword = (keyword: string): boolean => {
@@ -164,8 +160,38 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule, processId, proc
setCategoryFilter('all');
}, [processFilter]);
+ // 품목이 다른 공정에 배정되었는지 확인 (현재 공정 제외)
+ const isAssignedToOtherProcess = useCallback((item: ItemOption): boolean => {
+ if (!item.assignedProcesses?.length) return false;
+ if (!processId) return item.assignedProcesses.length > 0;
+ return item.assignedProcesses.some((ap) => String(ap.processId) !== processId);
+ }, [processId]);
+
+ // 이미 등록된 품목인지 확인
+ const isAlreadyRegistered = useCallback((item: ItemOption): boolean => {
+ return registeredItemIds?.has(item.id) ?? false;
+ }, [registeredItemIds]);
+
+ // 품목 선택 불가 여부
+ const isItemDisabled = useCallback((item: ItemOption): boolean => {
+ return isAssignedToOtherProcess(item) || isAlreadyRegistered(item);
+ }, [isAssignedToOtherProcess, isAlreadyRegistered]);
+
+ // 배정된 공정명 (현재 공정 제외)
+ const getAssignedProcessName = useCallback((item: ItemOption): string | null => {
+ if (!item.assignedProcesses?.length) return null;
+ const otherProcesses = processId
+ ? item.assignedProcesses.filter((ap) => String(ap.processId) !== processId)
+ : item.assignedProcesses;
+ if (otherProcesses.length === 0) return null;
+ return otherProcesses.map((ap) => ap.processName).join(', ');
+ }, [processId]);
+
// 체크박스 토글
const handleToggleItem = (id: string) => {
+ const item = itemList.find((i) => i.id === id);
+ if (item && isItemDisabled(item)) return;
+
setSelectedItemIds((prev) => {
const newSet = new Set(prev);
if (newSet.has(id)) {
@@ -285,50 +311,58 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule, processId, proc
품목유형
품목코드
품목명
- {/* 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 표시
- {item.processName || '-'}
- {item.processCategory || '-'}
- */}
-
- ))
+ itemList.map((item) => {
+ const disabled = isItemDisabled(item);
+ const assignedName = getAssignedProcessName(item);
+ const isRegistered = isAlreadyRegistered(item);
+ return (
+ !disabled && handleToggleItem(item.id)}
+ >
+
+ handleToggleItem(item.id)}
+ onClick={(e) => e.stopPropagation()}
+ disabled={disabled}
+ />
+
+ {item.type}
+ {item.code}
+ {item.fullName}
+
+ {isRegistered ? (
+ 현재 공정
+ ) : assignedName ? (
+ {assignedName}
+ ) : (
+ '-'
+ )}
+
+
+ );
+ })
)}
diff --git a/src/components/process-management/actions.ts b/src/components/process-management/actions.ts
index b6f5e314..cc736015 100644
--- a/src/components/process-management/actions.ts
+++ b/src/components/process-management/actions.ts
@@ -483,29 +483,25 @@ export interface ItemOption {
id: string;
fullName: string;
type: string;
- // TODO: API 응답에 process_name, process_category 필드 추가 후 활성화
- processName?: string;
- processCategory?: string;
+ /** 배정된 공정 목록 */
+ assignedProcesses?: Array<{ processId: number; processName: string }>;
}
interface GetItemListParams {
q?: string;
itemType?: string;
size?: number;
- /** 해당 공정 외 다른 공정에 이미 배정된 품목 제외 (공정 ID) */
+ /** 공정 배정 정보 포함을 위한 현재 공정 ID */
excludeProcessId?: string;
}
/**
* 품목 목록 조회 (분류 규칙용)
- * - excludeProcessId: 다른 공정에 이미 배정된 품목 제외 (중복 방지)
- *
- * TODO: 백엔드 API 수정 요청
- * - 응답에 process_name, process_category 필드 추가 필요 (공정 품목 선택 팝업에서 공정/구분 컬럼 표시용)
- * - 파라미터에 processName, processCategory 필터 추가 필요 (공정/구분 필터링용)
+ * - excludeProcessId: 공정 배정 정보를 포함하여 응답 (다른 공정 배정 품목은 disabled 처리용)
*/
export async function getItemList(params?: GetItemListParams): Promise {
- interface ItemListResponse { data: Array<{ id: number; name: string; item_code?: string; item_type?: string; item_type_name?: string }> }
+ interface AssignedProcess { process_id: number; process_name: string }
+ interface ItemListResponse { data: Array<{ id: number; name: string; item_code?: string; item_type?: string; item_type_name?: string; assigned_processes?: AssignedProcess[] }> }
const result = await executeServerAction({
url: buildApiUrl('/api/v1/items', {
size: params?.size || 1000,
@@ -523,6 +519,10 @@ export async function getItemList(params?: GetItemListParams): Promise ({
+ processId: ap.process_id,
+ processName: ap.process_name,
+ })),
}));
}