"use client"; import { useState, useCallback, useMemo, useRef } from "react"; import { apiClient } from "@/lib/api/client"; import { ItemRoutingConfig, ItemData, RoutingVersionData, RoutingDetailData, ColumnDef } from "../types"; import { defaultConfig } from "../config"; const API_BASE = "/process-work-standard"; /** 표시 컬럼 목록에서 기본(item_name, item_code) 외 추가 컬럼만 추출 */ function getExtraColumnNames(columns?: ColumnDef[]): string { if (!columns || columns.length === 0) return ""; return columns .map((c) => c.name) .filter((n) => n && n !== "item_name" && n !== "item_code") .join(","); } export function useItemRouting(configPartial: Partial) { const configKey = useMemo( () => JSON.stringify(configPartial), [configPartial] ); const config: ItemRoutingConfig = useMemo(() => ({ ...defaultConfig, ...configPartial, dataSource: { ...defaultConfig.dataSource, ...configPartial?.dataSource }, modals: { ...defaultConfig.modals, ...configPartial?.modals }, processColumns: configPartial?.processColumns?.length ? configPartial.processColumns : defaultConfig.processColumns, }), [configKey]); const configRef = useRef(config); configRef.current = config; const [items, setItems] = useState([]); const [allItems, setAllItems] = useState([]); const [versions, setVersions] = useState([]); const [details, setDetails] = useState([]); const [loading, setLoading] = useState(false); const [selectedItemCode, setSelectedItemCode] = useState(null); const [selectedItemName, setSelectedItemName] = useState(null); const [selectedVersionId, setSelectedVersionId] = useState(null); const isRegisteredMode = config.itemListMode === "registered"; /** API 기본 파라미터 생성 */ const buildBaseParams = useCallback((search?: string, columns?: ColumnDef[]) => { const ds = configRef.current.dataSource; const extra = getExtraColumnNames(columns); const filters = configRef.current.itemFilterConditions; const params: Record = { tableName: ds.itemTable, nameColumn: ds.itemNameColumn, codeColumn: ds.itemCodeColumn, routingTable: ds.routingVersionTable, routingFkColumn: ds.routingVersionFkColumn, }; if (search) params.search = search; if (extra) params.extraColumns = extra; if (filters && filters.length > 0) { params.filterConditions = JSON.stringify(filters); } return new URLSearchParams(params); }, []); // ──────────────────────────────────────── // 품목 목록 조회 (all 모드) // ──────────────────────────────────────── const fetchItems = useCallback( async (search?: string) => { try { setLoading(true); const cols = configRef.current.itemDisplayColumns; const params = buildBaseParams(search, cols); const res = await apiClient.get(`${API_BASE}/items?${params}`); if (res.data?.success) { const data = res.data.data || []; if (configRef.current.itemListMode !== "registered") { setItems(data); } return data; } } catch (err) { console.error("품목 조회 실패", err); } finally { setLoading(false); } return []; }, // eslint-disable-next-line react-hooks/exhaustive-deps [configKey, buildBaseParams] ); // ──────────────────────────────────────── // 등록 품목 조회 (registered 모드) // ──────────────────────────────────────── const fetchRegisteredItems = useCallback( async (search?: string) => { const screenCode = configRef.current.screenCode; if (!screenCode) { console.warn("screenCode가 설정되지 않았습니다"); setItems([]); return; } try { setLoading(true); const ds = configRef.current.dataSource; const cols = configRef.current.itemDisplayColumns; const extra = getExtraColumnNames(cols); const params = new URLSearchParams({ tableName: ds.itemTable, nameColumn: ds.itemNameColumn, codeColumn: ds.itemCodeColumn, routingTable: ds.routingVersionTable, routingFkColumn: ds.routingVersionFkColumn, ...(search ? { search } : {}), ...(extra ? { extraColumns: extra } : {}), }); const res = await apiClient.get( `${API_BASE}/registered-items/${encodeURIComponent(screenCode)}?${params}` ); if (res.data?.success) { setItems(res.data.data || []); } } catch (err) { console.error("등록 품목 조회 실패", err); } finally { setLoading(false); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [configKey] ); // ──────────────────────────────────────── // 전체 품목 조회 (등록 팝업용 - 필터+추가컬럼 적용) // ──────────────────────────────────────── const fetchAllItems = useCallback( async (search?: string) => { try { const cols = configRef.current.modalDisplayColumns; const params = buildBaseParams(search, cols); const res = await apiClient.get(`${API_BASE}/items?${params}`); if (res.data?.success) { setAllItems(res.data.data || []); } } catch (err) { console.error("전체 품목 조회 실패", err); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [configKey, buildBaseParams] ); // ──────────────────────────────────────── // 품목 등록/제거 (registered 모드) // ──────────────────────────────────────── const registerItem = useCallback( async (itemId: string, itemCode: string) => { const screenCode = configRef.current.screenCode; if (!screenCode) return false; try { const res = await apiClient.post(`${API_BASE}/registered-items`, { screenCode, itemId, itemCode, }); if (res.data?.success) { await fetchRegisteredItems(); return true; } } catch (err) { console.error("품목 등록 실패", err); } return false; }, [fetchRegisteredItems] ); const registerItemsBatch = useCallback( async (itemList: { itemId: string; itemCode: string }[]) => { const screenCode = configRef.current.screenCode; if (!screenCode) return false; try { const res = await apiClient.post(`${API_BASE}/registered-items/batch`, { screenCode, items: itemList, }); if (res.data?.success) { await fetchRegisteredItems(); return true; } } catch (err) { console.error("품목 일괄 등록 실패", err); } return false; }, [fetchRegisteredItems] ); const unregisterItem = useCallback( async (registeredId: string) => { try { const res = await apiClient.delete(`${API_BASE}/registered-items/${registeredId}`); if (res.data?.success) { if (selectedItemCode) { const removedItem = items.find((i) => i.registered_id === registeredId); if (removedItem) { const removedCode = removedItem.item_code || removedItem[configRef.current.dataSource.itemCodeColumn]; if (selectedItemCode === removedCode) { setSelectedItemCode(null); setSelectedItemName(null); setSelectedVersionId(null); setVersions([]); setDetails([]); } } } await fetchRegisteredItems(); return true; } } catch (err) { console.error("등록 품목 제거 실패", err); } return false; }, [selectedItemCode, items, fetchRegisteredItems] ); // ──────────────────────────────────────── // 라우팅 버전/공정 관련 (기존 동일) // ──────────────────────────────────────── const fetchVersions = useCallback( async (itemCode: string) => { try { const ds = configRef.current.dataSource; const params = new URLSearchParams({ routingVersionTable: ds.routingVersionTable, routingDetailTable: ds.routingDetailTable, routingFkColumn: ds.routingVersionFkColumn, processTable: ds.processTable, processNameColumn: ds.processNameColumn, processCodeColumn: ds.processCodeColumn, }); const res = await apiClient.get( `${API_BASE}/items/${encodeURIComponent(itemCode)}/routings?${params}` ); if (res.data?.success) { const routingData = res.data.data || []; setVersions(routingData); return routingData; } } catch (err) { console.error("라우팅 버전 조회 실패", err); } return []; }, // eslint-disable-next-line react-hooks/exhaustive-deps [configKey] ); const fetchDetails = useCallback( async (versionId: string) => { try { setLoading(true); const ds = configRef.current.dataSource; const searchConditions = { [ds.routingDetailFkColumn]: { value: versionId, operator: "equals" }, }; const params = new URLSearchParams({ page: "1", size: "1000", search: JSON.stringify(searchConditions), sortBy: "seq_no", sortOrder: "ASC", enableEntityJoin: "true", }); const res = await apiClient.get( `/table-management/tables/${ds.routingDetailTable}/data-with-joins?${params}` ); if (res.data?.success) { const result = res.data.data; setDetails(Array.isArray(result) ? result : result?.data || []); } } catch (err) { console.error("공정 상세 조회 실패", err); } finally { setLoading(false); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [configKey] ); const selectItem = useCallback( async (itemCode: string, itemName: string) => { setSelectedItemCode(itemCode); setSelectedItemName(itemName); setSelectedVersionId(null); setDetails([]); const versionList = await fetchVersions(itemCode); if (versionList.length > 0) { const defaultVersion = versionList.find((v: RoutingVersionData) => v.is_default); const targetVersion = defaultVersion || (configRef.current.autoSelectFirstVersion ? versionList[0] : null); if (targetVersion) { setSelectedVersionId(targetVersion.id); await fetchDetails(targetVersion.id); } } }, [fetchVersions, fetchDetails] ); const selectVersion = useCallback( async (versionId: string) => { setSelectedVersionId(versionId); await fetchDetails(versionId); }, [fetchDetails] ); const refreshVersions = useCallback(async () => { if (selectedItemCode) { const versionList = await fetchVersions(selectedItemCode); if (selectedVersionId) { await fetchDetails(selectedVersionId); } else if (versionList.length > 0) { const lastVersion = versionList[versionList.length - 1]; setSelectedVersionId(lastVersion.id); await fetchDetails(lastVersion.id); } } }, [selectedItemCode, selectedVersionId, fetchVersions, fetchDetails]); const refreshDetails = useCallback(async () => { if (selectedVersionId) { await fetchDetails(selectedVersionId); } }, [selectedVersionId, fetchDetails]); const deleteDetail = useCallback( async (detailId: string) => { try { const ds = configRef.current.dataSource; const res = await apiClient.delete( `/table-management/tables/${ds.routingDetailTable}/delete`, { data: [{ id: detailId }] } ); if (res.data?.success) { await refreshDetails(); return true; } } catch (err) { console.error("공정 삭제 실패", err); } return false; }, [refreshDetails] ); const deleteVersion = useCallback( async (versionId: string) => { try { const ds = configRef.current.dataSource; const res = await apiClient.delete( `/table-management/tables/${ds.routingVersionTable}/delete`, { data: [{ id: versionId }] } ); if (res.data?.success) { if (selectedVersionId === versionId) { setSelectedVersionId(null); setDetails([]); } await refreshVersions(); return true; } } catch (err) { console.error("버전 삭제 실패", err); } return false; }, [selectedVersionId, refreshVersions] ); const setDefaultVersion = useCallback( async (versionId: string) => { try { const ds = configRef.current.dataSource; const res = await apiClient.put(`${API_BASE}/versions/${versionId}/set-default`, { routingVersionTable: ds.routingVersionTable, routingFkColumn: ds.routingVersionFkColumn, }); if (res.data?.success) { if (selectedItemCode) await fetchVersions(selectedItemCode); return true; } } catch (err) { console.error("기본 버전 설정 실패", err); } return false; }, [selectedItemCode, fetchVersions] ); const unsetDefaultVersion = useCallback( async (versionId: string) => { try { const ds = configRef.current.dataSource; const res = await apiClient.put(`${API_BASE}/versions/${versionId}/unset-default`, { routingVersionTable: ds.routingVersionTable, }); if (res.data?.success) { if (selectedItemCode) await fetchVersions(selectedItemCode); return true; } } catch (err) { console.error("기본 버전 해제 실패", err); } return false; }, [selectedItemCode, fetchVersions] ); return { config, items, allItems, versions, details, loading, selectedItemCode, selectedItemName, selectedVersionId, isRegisteredMode, fetchItems, fetchRegisteredItems, fetchAllItems, registerItem, registerItemsBatch, unregisterItem, selectItem, selectVersion, refreshVersions, refreshDetails, deleteDetail, deleteVersion, setDefaultVersion, unsetDefaultVersion, }; }