337 lines
9.3 KiB
TypeScript
337 lines
9.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useCallback } from "react";
|
|
import { apiClient } from "@/lib/api/client";
|
|
import {
|
|
ProcessWorkStandardConfig,
|
|
ItemData,
|
|
RoutingVersion,
|
|
WorkItem,
|
|
WorkItemDetail,
|
|
SelectionState,
|
|
} from "../types";
|
|
|
|
const API_BASE = "/api/process-work-standard";
|
|
|
|
export function useProcessWorkStandard(config: ProcessWorkStandardConfig) {
|
|
const [items, setItems] = useState<ItemData[]>([]);
|
|
const [routings, setRoutings] = useState<RoutingVersion[]>([]);
|
|
const [workItems, setWorkItems] = useState<WorkItem[]>([]);
|
|
const [selectedWorkItemDetails, setSelectedWorkItemDetails] = useState<WorkItemDetail[]>([]);
|
|
const [selectedWorkItemId, setSelectedWorkItemId] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
const [selection, setSelection] = useState<SelectionState>({
|
|
itemCode: null,
|
|
itemName: null,
|
|
routingVersionId: null,
|
|
routingVersionName: null,
|
|
routingDetailId: null,
|
|
processName: null,
|
|
});
|
|
|
|
// 품목 목록 조회
|
|
const fetchItems = useCallback(
|
|
async (search?: string) => {
|
|
try {
|
|
setLoading(true);
|
|
const ds = config.dataSource;
|
|
const params = new URLSearchParams({
|
|
tableName: ds.itemTable,
|
|
nameColumn: ds.itemNameColumn,
|
|
codeColumn: ds.itemCodeColumn,
|
|
routingTable: ds.routingVersionTable,
|
|
routingFkColumn: ds.routingFkColumn,
|
|
...(search ? { search } : {}),
|
|
});
|
|
const res = await apiClient.get(`${API_BASE}/items?${params}`);
|
|
if (res.data?.success) {
|
|
setItems(res.data.data);
|
|
}
|
|
} catch (err) {
|
|
console.error("품목 조회 실패", err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
},
|
|
[config.dataSource]
|
|
);
|
|
|
|
// 라우팅 + 공정 조회
|
|
const fetchRoutings = useCallback(
|
|
async (itemCode: string) => {
|
|
try {
|
|
const ds = config.dataSource;
|
|
const params = new URLSearchParams({
|
|
routingVersionTable: ds.routingVersionTable,
|
|
routingDetailTable: ds.routingDetailTable,
|
|
routingFkColumn: ds.routingFkColumn,
|
|
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) {
|
|
setRoutings(res.data.data);
|
|
}
|
|
} catch (err) {
|
|
console.error("라우팅 조회 실패", err);
|
|
}
|
|
},
|
|
[config.dataSource]
|
|
);
|
|
|
|
// 작업 항목 조회
|
|
const fetchWorkItems = useCallback(async (routingDetailId: string) => {
|
|
try {
|
|
setLoading(true);
|
|
const res = await apiClient.get(
|
|
`${API_BASE}/routing-detail/${routingDetailId}/work-items`
|
|
);
|
|
if (res.data?.success) {
|
|
setWorkItems(res.data.items || []);
|
|
}
|
|
} catch (err) {
|
|
console.error("작업 항목 조회 실패", err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// 작업 항목 상세 조회
|
|
const fetchWorkItemDetails = useCallback(async (workItemId: string) => {
|
|
try {
|
|
const res = await apiClient.get(
|
|
`${API_BASE}/work-items/${workItemId}/details`
|
|
);
|
|
if (res.data?.success) {
|
|
setSelectedWorkItemDetails(res.data.data);
|
|
setSelectedWorkItemId(workItemId);
|
|
}
|
|
} catch (err) {
|
|
console.error("상세 조회 실패", err);
|
|
}
|
|
}, []);
|
|
|
|
// 품목 선택
|
|
const selectItem = useCallback(
|
|
async (itemCode: string, itemName: string) => {
|
|
setSelection((prev) => ({
|
|
...prev,
|
|
itemCode,
|
|
itemName,
|
|
routingVersionId: null,
|
|
routingVersionName: null,
|
|
routingDetailId: null,
|
|
processName: null,
|
|
}));
|
|
setWorkItems([]);
|
|
setSelectedWorkItemDetails([]);
|
|
setSelectedWorkItemId(null);
|
|
await fetchRoutings(itemCode);
|
|
},
|
|
[fetchRoutings]
|
|
);
|
|
|
|
// 공정 선택
|
|
const selectProcess = useCallback(
|
|
async (
|
|
routingDetailId: string,
|
|
processName: string,
|
|
routingVersionId: string,
|
|
routingVersionName: string
|
|
) => {
|
|
setSelection((prev) => ({
|
|
...prev,
|
|
routingVersionId,
|
|
routingVersionName,
|
|
routingDetailId,
|
|
processName,
|
|
}));
|
|
setSelectedWorkItemDetails([]);
|
|
setSelectedWorkItemId(null);
|
|
await fetchWorkItems(routingDetailId);
|
|
},
|
|
[fetchWorkItems]
|
|
);
|
|
|
|
// 작업 항목 추가
|
|
const createWorkItem = useCallback(
|
|
async (data: {
|
|
work_phase: string;
|
|
title: string;
|
|
is_required: string;
|
|
description?: string;
|
|
details?: Array<{
|
|
detail_type?: string;
|
|
content: string;
|
|
is_required: string;
|
|
sort_order: number;
|
|
}>;
|
|
}) => {
|
|
if (!selection.routingDetailId) return null;
|
|
|
|
try {
|
|
const nextOrder =
|
|
workItems.filter((wi) => wi.work_phase === data.work_phase).length + 1;
|
|
|
|
const res = await apiClient.post(`${API_BASE}/work-items`, {
|
|
routing_detail_id: selection.routingDetailId,
|
|
work_phase: data.work_phase,
|
|
title: data.title,
|
|
is_required: data.is_required,
|
|
sort_order: nextOrder,
|
|
description: data.description,
|
|
});
|
|
|
|
if (res.data?.success && res.data.data) {
|
|
const newItem = res.data.data;
|
|
|
|
// 상세 항목도 함께 생성
|
|
if (data.details && data.details.length > 0) {
|
|
for (const detail of data.details) {
|
|
await apiClient.post(`${API_BASE}/work-item-details`, {
|
|
work_item_id: newItem.id,
|
|
...detail,
|
|
});
|
|
}
|
|
}
|
|
|
|
await fetchWorkItems(selection.routingDetailId);
|
|
return newItem;
|
|
}
|
|
} catch (err) {
|
|
console.error("작업 항목 생성 실패", err);
|
|
}
|
|
return null;
|
|
},
|
|
[selection.routingDetailId, workItems, fetchWorkItems]
|
|
);
|
|
|
|
// 작업 항목 수정
|
|
const updateWorkItem = useCallback(
|
|
async (id: string, data: Partial<WorkItem>) => {
|
|
try {
|
|
const res = await apiClient.put(`${API_BASE}/work-items/${id}`, data);
|
|
if (res.data?.success && selection.routingDetailId) {
|
|
await fetchWorkItems(selection.routingDetailId);
|
|
}
|
|
} catch (err) {
|
|
console.error("작업 항목 수정 실패", err);
|
|
}
|
|
},
|
|
[selection.routingDetailId, fetchWorkItems]
|
|
);
|
|
|
|
// 작업 항목 삭제
|
|
const deleteWorkItem = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
const res = await apiClient.delete(`${API_BASE}/work-items/${id}`);
|
|
if (res.data?.success && selection.routingDetailId) {
|
|
await fetchWorkItems(selection.routingDetailId);
|
|
if (selectedWorkItemId === id) {
|
|
setSelectedWorkItemDetails([]);
|
|
setSelectedWorkItemId(null);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("작업 항목 삭제 실패", err);
|
|
}
|
|
},
|
|
[selection.routingDetailId, selectedWorkItemId, fetchWorkItems]
|
|
);
|
|
|
|
// 상세 추가
|
|
const createDetail = useCallback(
|
|
async (workItemId: string, data: Partial<WorkItemDetail>) => {
|
|
try {
|
|
const res = await apiClient.post(`${API_BASE}/work-item-details`, {
|
|
work_item_id: workItemId,
|
|
...data,
|
|
});
|
|
if (res.data?.success) {
|
|
await fetchWorkItemDetails(workItemId);
|
|
if (selection.routingDetailId) {
|
|
await fetchWorkItems(selection.routingDetailId);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("상세 생성 실패", err);
|
|
}
|
|
},
|
|
[fetchWorkItemDetails, fetchWorkItems, selection.routingDetailId]
|
|
);
|
|
|
|
// 상세 수정
|
|
const updateDetail = useCallback(
|
|
async (id: string, data: Partial<WorkItemDetail>) => {
|
|
try {
|
|
const res = await apiClient.put(
|
|
`${API_BASE}/work-item-details/${id}`,
|
|
data
|
|
);
|
|
if (res.data?.success && selectedWorkItemId) {
|
|
await fetchWorkItemDetails(selectedWorkItemId);
|
|
}
|
|
} catch (err) {
|
|
console.error("상세 수정 실패", err);
|
|
}
|
|
},
|
|
[selectedWorkItemId, fetchWorkItemDetails]
|
|
);
|
|
|
|
// 상세 삭제
|
|
const deleteDetail = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
const res = await apiClient.delete(
|
|
`${API_BASE}/work-item-details/${id}`
|
|
);
|
|
if (res.data?.success) {
|
|
if (selectedWorkItemId) {
|
|
await fetchWorkItemDetails(selectedWorkItemId);
|
|
}
|
|
if (selection.routingDetailId) {
|
|
await fetchWorkItems(selection.routingDetailId);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("상세 삭제 실패", err);
|
|
}
|
|
},
|
|
[
|
|
selectedWorkItemId,
|
|
selection.routingDetailId,
|
|
fetchWorkItemDetails,
|
|
fetchWorkItems,
|
|
]
|
|
);
|
|
|
|
return {
|
|
items,
|
|
routings,
|
|
workItems,
|
|
selectedWorkItemDetails,
|
|
selectedWorkItemId,
|
|
selection,
|
|
loading,
|
|
saving,
|
|
fetchItems,
|
|
selectItem,
|
|
selectProcess,
|
|
fetchWorkItems,
|
|
fetchWorkItemDetails,
|
|
setSelectedWorkItemId,
|
|
createWorkItem,
|
|
updateWorkItem,
|
|
deleteWorkItem,
|
|
createDetail,
|
|
updateDetail,
|
|
deleteDetail,
|
|
};
|
|
}
|