From 2ef02e27dbe639876fa3b90c30307dda183545ec Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 17 Jul 2025 10:50:52 +0000 Subject: (최겸) 기술영업 변경사항 적용 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/items-tech/table/add-items-dialog.tsx | 108 ++++++++++++--------- lib/items-tech/table/hull/import-item-handler.tsx | 10 +- lib/items-tech/table/hull/item-excel-template.tsx | 10 -- .../table/hull/offshore-hull-table-columns.tsx | 4 +- .../hull/offshore-hull-table-toolbar-actions.tsx | 8 +- lib/items-tech/table/import-excel-button.tsx | 16 +-- lib/items-tech/table/ship/Items-ship-table.tsx | 1 + lib/items-tech/table/ship/import-item-handler.tsx | 32 ++++-- .../table/ship/items-ship-table-columns.tsx | 4 +- .../table/ship/items-table-toolbar-actions.tsx | 9 +- lib/items-tech/table/top/import-item-handler.tsx | 28 ++++-- lib/items-tech/table/top/item-excel-template.tsx | 7 -- .../table/top/offshore-top-table-columns.tsx | 4 +- .../top/offshore-top-table-toolbar-actions.tsx | 8 +- lib/items-tech/table/update-items-sheet.tsx | 7 +- lib/items-tech/validations.ts | 18 ++-- 16 files changed, 154 insertions(+), 120 deletions(-) (limited to 'lib/items-tech') diff --git a/lib/items-tech/table/add-items-dialog.tsx b/lib/items-tech/table/add-items-dialog.tsx index ee8ee8b8..1b0d00c7 100644 --- a/lib/items-tech/table/add-items-dialog.tsx +++ b/lib/items-tech/table/add-items-dialog.tsx @@ -4,7 +4,7 @@ import * as React from "react" import { useRouter } from "next/navigation" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" -import { Plus } from "lucide-react" +import { Plus, Loader } from "lucide-react" import * as z from "zod" import { Button } from "@/components/ui/button" @@ -44,6 +44,7 @@ const shipbuildingWorkTypes = [ { label: "선실", value: "선실" }, { label: "배관", value: "배관" }, { label: "철의", value: "철의" }, + { label: "선체", value: "선체" }, ] as const // 해양 TOP 공종 유형 정의 @@ -60,12 +61,14 @@ const offshoreHullWorkTypes = [ { label: "HE", value: "HE" }, { label: "HH", value: "HH" }, { label: "HM", value: "HM" }, + { label: "HO", value: "HO" }, + { label: "HP", value: "HP" }, { label: "NC", value: "NC" }, ] as const // 기본 아이템 스키마 const itemFormSchema = z.object({ - itemCode: z.string().min(1, "아이템 코드는 필수입니다"), + itemCode: z.string().optional(), workType: z.string().min(1, "공종은 필수입니다"), // 조선 및 해양 아이템 공통 필드 itemList: z.string().optional(), @@ -83,6 +86,7 @@ interface AddItemDialogProps { export function AddItemDialog({ itemType }: AddItemDialogProps) { const router = useRouter() const [open, setOpen] = React.useState(false) + const [isAddPending, startAddTransition] = React.useTransition() // 기본값 설정 const getDefaultValues = () => { @@ -120,53 +124,55 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { }) const onSubmit = async (data: ItemFormValues) => { - try { - switch (itemType) { - case 'shipbuilding': - if (!data.shipTypes) { - toast.error("선종은 필수입니다") - return - } + startAddTransition(async () => { + try { + switch (itemType) { + case 'shipbuilding': + if (!data.shipTypes) { + toast.error("선종은 필수입니다") + return + } + + await createShipbuildingItem({ + itemCode: data.itemCode || "", + workType: data.workType as "기장" | "전장" | "선실" | "배관" | "철의" | "선체", + shipTypes: data.shipTypes, + itemList: data.itemList || null, + }); + break; - await createShipbuildingItem({ - itemCode: data.itemCode, - workType: data.workType, - shipTypes: data.shipTypes, - itemList: data.itemList || null, - }); - break; - - case 'offshoreTop': - await createOffshoreTopItem({ - itemCode: data.itemCode, - workType: data.workType as "TM" | "TS" | "TE" | "TP", - itemList: data.itemList || null, - subItemList: data.subItemList || null - }); - break; - - case 'offshoreHull': - await createOffshoreHullItem({ - itemCode: data.itemCode, - workType: data.workType as "HA" | "HE" | "HH" | "HM" | "NC", - itemList: data.itemList || null, - subItemList: data.subItemList || null - }); - break; + case 'offshoreTop': + await createOffshoreTopItem({ + itemCode: data.itemCode || "", + workType: data.workType as "TM" | "TS" | "TE" | "TP", + itemList: data.itemList || null, + subItemList: data.subItemList || null + }); + break; + + case 'offshoreHull': + await createOffshoreHullItem({ + itemCode: data.itemCode || "", + workType: data.workType as "HA" | "HE" | "HH" | "HM" | "HO" | "HP" | "NC", + itemList: data.itemList || null, + subItemList: data.subItemList || null + }); + break; + + default: + toast.error("지원하지 않는 아이템 타입입니다"); + return; + } - default: - toast.error("지원하지 않는 아이템 타입입니다"); - return; + toast.success("아이템이 성공적으로 추가되었습니다") + setOpen(false) + form.reset(getDefaultValues()) + router.refresh() + } catch (error) { + toast.error("아이템 추가 중 오류가 발생했습니다") + console.error(error) } - - toast.success("아이템이 성공적으로 추가되었습니다") - setOpen(false) - form.reset(getDefaultValues()) - router.refresh() - } catch (error) { - toast.error("아이템 추가 중 오류가 발생했습니다") - console.error(error) - } + }) } const getItemTypeLabel = () => { @@ -315,7 +321,15 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { - + diff --git a/lib/items-tech/table/hull/import-item-handler.tsx b/lib/items-tech/table/hull/import-item-handler.tsx index aa0c7992..8c8fc57d 100644 --- a/lib/items-tech/table/hull/import-item-handler.tsx +++ b/lib/items-tech/table/hull/import-item-handler.tsx @@ -4,11 +4,11 @@ import { z } from "zod" import { createOffshoreHullItem } from "../../service" // 해양 HULL 기능(공종) 유형 enum -const HULL_WORK_TYPES = ["HA", "HE", "HH", "HM", "NC"] as const; +const HULL_WORK_TYPES = ["HA", "HE", "HH", "HM", "HO", "HP", "NC"] as const; // 아이템 데이터 검증을 위한 Zod 스키마 const itemSchema = z.object({ - itemCode: z.string().min(1, "아이템 코드는 필수입니다"), + itemCode: z.string().optional(), workType: z.enum(HULL_WORK_TYPES, { required_error: "기능(공종)은 필수입니다", }), @@ -19,7 +19,7 @@ const itemSchema = z.object({ interface ProcessResult { successCount: number; errorCount: number; - errors?: Array<{ row: number; message: string }>; + errors: Array<{ row: number; message: string; itemCode?: string; workType?: string }>; } /** @@ -45,7 +45,7 @@ export async function processHullFileImport( // 데이터 행이 없으면 빈 결과 반환 if (dataRows.length === 0) { - return { successCount: 0, errorCount: 0 }; + return { successCount: 0, errorCount: 0, errors: [] }; } // 각 행에 대해 처리 @@ -89,7 +89,7 @@ export async function processHullFileImport( // 해양 HULL 아이템 생성 const result = await createOffshoreHullItem({ itemCode: cleanedRow.itemCode, - workType: cleanedRow.workType as "HA" | "HE" | "HH" | "HM" | "NC", + workType: cleanedRow.workType as "HA" | "HE" | "HH" | "HM" | "HO" | "HP" | "NC", itemList: cleanedRow.itemList, subItemList: cleanedRow.subItemList, }); diff --git a/lib/items-tech/table/hull/item-excel-template.tsx b/lib/items-tech/table/hull/item-excel-template.tsx index 13ec1973..79512b9b 100644 --- a/lib/items-tech/table/hull/item-excel-template.tsx +++ b/lib/items-tech/table/hull/item-excel-template.tsx @@ -1,9 +1,6 @@ import * as ExcelJS from 'exceljs'; import { saveAs } from "file-saver"; -// 해양 HULL 기능(공종) 유형 -const HULL_WORK_TYPES = ["HA", "HE", "HH", "HM", "NC"] as const; - /** * 해양 HULL 아이템 데이터 가져오기를 위한 Excel 템플릿 파일 생성 및 다운로드 */ @@ -79,13 +76,6 @@ export async function exportHullItemTemplate() { } }); - // 워크시트에 공종 유형 관련 메모 추가 - const infoRow = worksheet.addRow(['공종 유형 안내: ' + HULL_WORK_TYPES.join(', ')]); - infoRow.font = { bold: true, color: { argb: 'FF0000FF' } }; - worksheet.mergeCells(`A${infoRow.number}:F${infoRow.number}`); - - - // 워크시트 보호 (선택적) worksheet.protect('', { selectLockedCells: true, diff --git a/lib/items-tech/table/hull/offshore-hull-table-columns.tsx b/lib/items-tech/table/hull/offshore-hull-table-columns.tsx index 19782c42..efc6c583 100644 --- a/lib/items-tech/table/hull/offshore-hull-table-columns.tsx +++ b/lib/items-tech/table/hull/offshore-hull-table-columns.tsx @@ -162,7 +162,7 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => ( ), - cell: ({ row }) => formatDate(row.original.createdAt, "KR"), + cell: ({ row }) => formatDate(row.original.createdAt), enableSorting: true, enableHiding: true, meta: { @@ -174,7 +174,7 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => ( ), - cell: ({ row }) => formatDate(row.original.updatedAt, "KR"), + cell: ({ row }) => formatDate(row.original.updatedAt), enableSorting: true, enableHiding: true, meta: { diff --git a/lib/items-tech/table/hull/offshore-hull-table-toolbar-actions.tsx b/lib/items-tech/table/hull/offshore-hull-table-toolbar-actions.tsx index ad6f86b2..642dfea7 100644 --- a/lib/items-tech/table/hull/offshore-hull-table-toolbar-actions.tsx +++ b/lib/items-tech/table/hull/offshore-hull-table-toolbar-actions.tsx @@ -70,12 +70,10 @@ export function OffshoreHullTableToolbarActions({ table }: OffshoreHullTableTool // 필요한 헤더 직접 정의 (필터링 문제 해결) const headers = [ - { key: 'itemCode', header: '아이템 코드' }, - { key: 'itemName', header: '아이템 명' }, - { key: 'description', header: '설명' }, + { key: 'itemCode', header: '자재 그룹' }, { key: 'workType', header: '기능(공종)' }, - { key: 'itemList', header: '아이템 리스트' }, - { key: 'subItemList', header: '서브 아이템 리스트' }, + { key: 'itemList', header: '자재명' }, + { key: 'subItemList', header: '자재명(상세)' }, ].filter(header => !excludeColumns.includes(header.key)); console.log("내보내기 헤더:", headers); diff --git a/lib/items-tech/table/import-excel-button.tsx b/lib/items-tech/table/import-excel-button.tsx index 02736664..4565c365 100644 --- a/lib/items-tech/table/import-excel-button.tsx +++ b/lib/items-tech/table/import-excel-button.tsx @@ -84,7 +84,6 @@ export function ImportItemButton({ itemType, onSuccess }: ImportItemButtonProps) // 복호화 실패 시 원본 파일 사용 arrayBuffer = await file.arrayBuffer(); } - // ExcelJS 워크북 로드 const workbook = new ExcelJS.Workbook(); await workbook.xlsx.load(arrayBuffer); @@ -94,21 +93,21 @@ export function ImportItemButton({ itemType, onSuccess }: ImportItemButtonProps) if (!worksheet) { throw new Error("Excel 파일에 워크시트가 없습니다."); } - // 헤더 행 찾기 let headerRowIndex = 1; let headerRow: ExcelJS.Row | undefined; let headerValues: (string | null)[] = []; + worksheet.eachRow((row, rowNumber) => { const values = row.values as (string | null)[]; - if (!headerRow && values.some(v => v === "자재 그룹" || v === "itemCode" || v === "item_code")) { + if (!headerRow && values.some(v => v === "자재 그룹" || v === "자재 코드" || v === "itemCode" || v === "item_code")) { headerRowIndex = rowNumber; headerRow = row; headerValues = [...values]; } }); - + if (!headerRow) { throw new Error("Excel 파일에서 헤더 행을 찾을 수 없습니다."); } @@ -173,7 +172,7 @@ export function ImportItemButton({ itemType, onSuccess }: ImportItemButtonProps) }; // 선택된 타입에 따라 적절한 프로세스 함수 호출 - let result; + let result: { successCount: number; errorCount: number; errors?: Array<{ row: number; message: string; itemCode?: string; workType?: string }> }; if (itemType === "top") { result = await processTopFileImport(dataRows, updateProgress); } else if (itemType === "hull") { @@ -185,7 +184,12 @@ export function ImportItemButton({ itemType, onSuccess }: ImportItemButtonProps) toast.success(`${result.successCount}개의 ${ITEM_TYPE_NAMES[itemType]}이(가) 성공적으로 가져와졌습니다.`); if (result.errorCount > 0) { - toast.warning(`${result.errorCount}개의 항목은 처리할 수 없었습니다.`); + const errorDetails = result.errors?.map((error: { row: number; message: string; itemCode?: string; workType?: string }) => + `행 ${error.row}: ${error.itemCode || '알 수 없음'} (${error.workType || '알 수 없음'}) - ${error.message}` + ).join('\n') || '오류 정보를 가져올 수 없습니다.'; + + console.error('Import 오류 상세:', errorDetails); + toast.error(`${result.errorCount}개의 항목 처리 실패. 콘솔에서 상세 내용을 확인하세요.`); } // 상태 초기화 및 다이얼로그 닫기 diff --git a/lib/items-tech/table/ship/Items-ship-table.tsx b/lib/items-tech/table/ship/Items-ship-table.tsx index feab288a..cf41b3cf 100644 --- a/lib/items-tech/table/ship/Items-ship-table.tsx +++ b/lib/items-tech/table/ship/Items-ship-table.tsx @@ -90,6 +90,7 @@ export function ItemsShipTable({ promises }: ItemsTableProps) { { label: "선실", value: "선실" }, { label: "배관", value: "배관" }, { label: "철의", value: "철의" }, + { label: "선체", value: "선체" }, ], }, { diff --git a/lib/items-tech/table/ship/import-item-handler.tsx b/lib/items-tech/table/ship/import-item-handler.tsx index a47e451b..57546cc6 100644 --- a/lib/items-tech/table/ship/import-item-handler.tsx +++ b/lib/items-tech/table/ship/import-item-handler.tsx @@ -5,8 +5,8 @@ import { createShipbuildingImportItem } from "../../service" // 아이템 생성 // 아이템 데이터 검증을 위한 Zod 스키마 const itemSchema = z.object({ - itemCode: z.string().min(1, "아이템 코드는 필수입니다"), - workType: z.enum(["기장", "전장", "선실", "배관", "철의"], { + itemCode: z.string().optional(), + workType: z.enum(["기장", "전장", "선실", "배관", "철의", "선체"], { required_error: "기능(공종)은 필수입니다", }), shipTypes: z.string().nullable().optional(), @@ -16,7 +16,7 @@ const itemSchema = z.object({ interface ProcessResult { successCount: number; errorCount: number; - errors?: Array<{ row: number; message: string }>; + errors: Array<{ row: number; message: string; itemCode?: string; workType?: string }>; } /** @@ -42,7 +42,7 @@ export async function processFileImport( // 데이터 행이 없으면 빈 결과 반환 if (dataRows.length === 0) { - return { successCount: 0, errorCount: 0 }; + return { successCount: 0, errorCount: 0, errors: [] }; } // 각 행에 대해 처리 @@ -78,7 +78,12 @@ export async function processFileImport( err => `${err.path.join('.')}: ${err.message}` ).join(', '); - errors.push({ row: rowIndex, message: errorMessage }); + errors.push({ + row: rowIndex, + message: errorMessage, + itemCode: cleanedRow.itemCode, + workType: cleanedRow.workType + }); errorCount++; continue; } @@ -86,7 +91,7 @@ export async function processFileImport( // 아이템 생성 const result = await createShipbuildingImportItem({ itemCode: cleanedRow.itemCode, - workType: cleanedRow.workType as "기장" | "전장" | "선실" | "배관" | "철의", + workType: cleanedRow.workType as "기장" | "전장" | "선실" | "배관" | "철의" | "선체", shipTypes: cleanedRow.shipTypes, itemList: cleanedRow.itemList, }); @@ -96,16 +101,25 @@ export async function processFileImport( } else { errors.push({ row: rowIndex, - message: result.message || result.error || "알 수 없는 오류" + message: result.message || result.error || "알 수 없는 오류", + itemCode: cleanedRow.itemCode, + workType: cleanedRow.workType }); errorCount++; } } catch (error) { console.error(`${rowIndex}행 처리 오류:`, error); + + // cleanedRow가 정의되지 않은 경우를 처리 + const itemCode = row["자재 그룹"] || row["itemCode"] || ""; + const workType = row["기능(공종)"] || row["workType"] || ""; + errors.push({ row: rowIndex, - message: error instanceof Error ? error.message : "알 수 없는 오류" + message: error instanceof Error ? error.message : "알 수 없는 오류", + itemCode: typeof itemCode === 'string' ? itemCode.trim() : String(itemCode).trim(), + workType: typeof workType === 'string' ? workType.trim() : String(workType).trim() }); errorCount++; } @@ -120,6 +134,6 @@ export async function processFileImport( return { successCount, errorCount, - errors: errors.length > 0 ? errors : undefined + errors }; } \ No newline at end of file diff --git a/lib/items-tech/table/ship/items-ship-table-columns.tsx b/lib/items-tech/table/ship/items-ship-table-columns.tsx index 7018ae43..13ba2480 100644 --- a/lib/items-tech/table/ship/items-ship-table-columns.tsx +++ b/lib/items-tech/table/ship/items-ship-table-columns.tsx @@ -165,7 +165,7 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => ( ), - cell: ({ row }) => formatDate(row.original.createdAt, "KR"), + cell: ({ row }) => formatDate(row.original.createdAt), enableSorting: true, enableHiding: true, meta: { @@ -177,7 +177,7 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum header: ({ column }) => ( ), - cell: ({ row }) => formatDate(row.original.updatedAt, "KR"), + cell: ({ row }) => formatDate(row.original.updatedAt), enableSorting: true, enableHiding: true, meta: { diff --git a/lib/items-tech/table/ship/items-table-toolbar-actions.tsx b/lib/items-tech/table/ship/items-table-toolbar-actions.tsx index e58ba135..29995327 100644 --- a/lib/items-tech/table/ship/items-table-toolbar-actions.tsx +++ b/lib/items-tech/table/ship/items-table-toolbar-actions.tsx @@ -23,7 +23,7 @@ import { ImportItemButton } from "../import-excel-button" interface ShipbuildingItem { id: number; itemId: number; - workType: "기장" | "전장" | "선실" | "배관" | "철의"; + workType: "기장" | "전장" | "선실" | "배관" | "철의" | "선체"; shipTypes: string; itemCode: string; itemName: string; @@ -70,12 +70,11 @@ export function ItemsTableToolbarActions({ table }: ItemsTableToolbarActionsProp // 필요한 헤더 직접 정의 (필터링 문제 해결) const headers = [ - { key: 'itemCode', header: '아이템 코드' }, - { key: 'itemName', header: '아이템 명' }, - { key: 'description', header: '설명' }, + { key: 'itemCode', header: '자재 그룹' }, { key: 'workType', header: '기능(공종)' }, { key: 'shipTypes', header: '선종' }, - { key: 'itemList', header: '아이템 리스트' } + { key: 'itemList', header: '자재명' }, + { key: 'subItemList', header: '자재명(상세)' }, ].filter(header => !excludeColumns.includes(header.key)); console.log("내보내기 헤더:", headers); diff --git a/lib/items-tech/table/top/import-item-handler.tsx b/lib/items-tech/table/top/import-item-handler.tsx index 541ec4ef..0a163791 100644 --- a/lib/items-tech/table/top/import-item-handler.tsx +++ b/lib/items-tech/table/top/import-item-handler.tsx @@ -8,7 +8,7 @@ const TOP_WORK_TYPES = ["TM", "TS", "TE", "TP"] as const; // 아이템 데이터 검증을 위한 Zod 스키마 const itemSchema = z.object({ - itemCode: z.string().min(1, "아이템 코드는 필수입니다"), + itemCode: z.string().optional(), workType: z.enum(TOP_WORK_TYPES, { required_error: "기능(공종)은 필수입니다", }), @@ -19,7 +19,7 @@ const itemSchema = z.object({ interface ProcessResult { successCount: number; errorCount: number; - errors?: Array<{ row: number; message: string }>; + errors: Array<{ row: number; message: string; itemCode?: string; workType?: string }>; } /** @@ -45,7 +45,7 @@ export async function processTopFileImport( // 데이터 행이 없으면 빈 결과 반환 if (dataRows.length === 0) { - return { successCount: 0, errorCount: 0 }; + return { successCount: 0, errorCount: 0, errors: [] }; } // 각 행에 대해 처리 @@ -81,7 +81,12 @@ export async function processTopFileImport( err => `${err.path.join('.')}: ${err.message}` ).join(', '); - errors.push({ row: rowIndex, message: errorMessage }); + errors.push({ + row: rowIndex, + message: errorMessage, + itemCode: cleanedRow.itemCode, + workType: cleanedRow.workType + }); errorCount++; continue; } @@ -99,15 +104,24 @@ export async function processTopFileImport( } else { errors.push({ row: rowIndex, - message: result.message || result.error || "알 수 없는 오류" + message: result.message || result.error || "알 수 없는 오류", + itemCode: cleanedRow.itemCode, + workType: cleanedRow.workType }); errorCount++; } } catch (error) { console.error(`${rowIndex}행 처리 오류:`, error); + + // cleanedRow가 정의되지 않은 경우를 처리 + const itemCode = row["자재 그룹"] || row["itemCode"] || ""; + const workType = row["기능(공종)"] || row["workType"] || ""; + errors.push({ row: rowIndex, - message: error instanceof Error ? error.message : "알 수 없는 오류" + message: error instanceof Error ? error.message : "알 수 없는 오류", + itemCode: typeof itemCode === 'string' ? itemCode.trim() : String(itemCode).trim(), + workType: typeof workType === 'string' ? workType.trim() : String(workType).trim() }); errorCount++; } @@ -122,6 +136,6 @@ export async function processTopFileImport( return { successCount, errorCount, - errors: errors.length > 0 ? errors : undefined + errors }; } diff --git a/lib/items-tech/table/top/item-excel-template.tsx b/lib/items-tech/table/top/item-excel-template.tsx index f547d617..b67d91be 100644 --- a/lib/items-tech/table/top/item-excel-template.tsx +++ b/lib/items-tech/table/top/item-excel-template.tsx @@ -1,8 +1,6 @@ import * as ExcelJS from 'exceljs'; import { saveAs } from "file-saver"; -// 해양 TOP 기능(공종) 유형 -const TOP_WORK_TYPES = ["TM", "TS", "TE", "TP"] as const; /** * 해양 TOP 아이템 데이터 가져오기를 위한 Excel 템플릿 파일 생성 및 다운로드 @@ -81,11 +79,6 @@ export async function exportTopItemTemplate() { } }); - // 워크시트에 공종 유형 관련 메모 추가 - const infoRow = worksheet.addRow(['공종 유형 안내: ' + TOP_WORK_TYPES.join(', ')]); - infoRow.font = { bold: true, color: { argb: 'FF0000FF' } }; - worksheet.mergeCells(`A${infoRow.number}:F${infoRow.number}`); - // 워크시트 보호 (선택적) worksheet.protect('', { diff --git a/lib/items-tech/table/top/offshore-top-table-columns.tsx b/lib/items-tech/table/top/offshore-top-table-columns.tsx index c2df4b75..93f27492 100644 --- a/lib/items-tech/table/top/offshore-top-table-columns.tsx +++ b/lib/items-tech/table/top/offshore-top-table-columns.tsx @@ -162,7 +162,7 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column header: ({ column }) => ( ), - cell: ({ row }) => formatDate(row.original.createdAt, "KR"), + cell: ({ row }) => formatDate(row.original.createdAt), enableSorting: true, enableHiding: true, meta: { @@ -174,7 +174,7 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column header: ({ column }) => ( ), - cell: ({ row }) => formatDate(row.original.updatedAt, "KR"), + cell: ({ row }) => formatDate(row.original.updatedAt), enableSorting: true, enableHiding: true, meta: { diff --git a/lib/items-tech/table/top/offshore-top-table-toolbar-actions.tsx b/lib/items-tech/table/top/offshore-top-table-toolbar-actions.tsx index f91adf96..bf10560f 100644 --- a/lib/items-tech/table/top/offshore-top-table-toolbar-actions.tsx +++ b/lib/items-tech/table/top/offshore-top-table-toolbar-actions.tsx @@ -70,12 +70,10 @@ export function OffshoreTopTableToolbarActions({ table }: OffshoreTopTableToolba // 필요한 헤더 직접 정의 (필터링 문제 해결) const headers = [ - { key: 'itemCode', header: '아이템 코드' }, - { key: 'itemName', header: '아이템 명' }, - { key: 'description', header: '설명' }, + { key: 'itemCode', header: '자재 그룹' }, { key: 'workType', header: '기능(공종)' }, - { key: 'itemList', header: '아이템 리스트' }, - { key: 'subItemList', header: '서브 아이템 리스트' }, + { key: 'itemList', header: '자재명' }, + { key: 'subItemList', header: '자재명(상세)' }, ].filter(header => !excludeColumns.includes(header.key)); console.log("내보내기 헤더:", headers); diff --git a/lib/items-tech/table/update-items-sheet.tsx b/lib/items-tech/table/update-items-sheet.tsx index 16dfcb71..978e83d5 100644 --- a/lib/items-tech/table/update-items-sheet.tsx +++ b/lib/items-tech/table/update-items-sheet.tsx @@ -44,6 +44,7 @@ const shipbuildingWorkTypes = [ { value: "선실", label: "선실" }, { value: "배관", label: "배관" }, { value: "철의", label: "철의" }, + { value: "선체", label: "선체" }, ] as const const offshoreTopWorkTypes = [ @@ -58,6 +59,8 @@ const offshoreHullWorkTypes = [ { value: "HE", label: "HE" }, { value: "HH", label: "HH" }, { value: "HM", label: "HM" }, + { value: "HO", label: "HO" }, + { value: "HP", label: "HP" }, { value: "NC", label: "NC" }, ] as const @@ -65,7 +68,7 @@ const offshoreHullWorkTypes = [ type ShipbuildingItem = { id: number itemCode: string - workType: "기장" | "전장" | "선실" | "배관" | "철의" + workType: "기장" | "전장" | "선실" | "배관" | "철의" | "선체" shipTypes: string itemList: string | null } @@ -81,7 +84,7 @@ type OffshoreTopItem = { type OffshoreHullItem = { id: number itemCode: string - workType: "HA" | "HE" | "HH" | "HM" | "NC" + workType: "HA" | "HE" | "HH" | "HM" | "HO" | "HP" | "NC" itemList: string | null subItemList: string | null } diff --git a/lib/items-tech/validations.ts b/lib/items-tech/validations.ts index 653f0af8..95a34b58 100644 --- a/lib/items-tech/validations.ts +++ b/lib/items-tech/validations.ts @@ -24,6 +24,8 @@ export const shipbuildingSearchParamsCache = createSearchParamsCache({ itemList: parseAsString.withDefault(""), filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + shipFilters: getFiltersStateParser().withDefault([]), + shipJoinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), search: parseAsString.withDefault(""), }) @@ -42,6 +44,8 @@ export const offshoreTopSearchParamsCache = createSearchParamsCache({ subItemList: parseAsString.withDefault(""), filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + topFilters: getFiltersStateParser().withDefault([]), + topJoinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), search: parseAsString.withDefault(""), }) @@ -59,6 +63,8 @@ export const offshoreHullSearchParamsCache = createSearchParamsCache({ subItemList: parseAsString.withDefault(""), filters: getFiltersStateParser().withDefault([]), joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), + hullFilters: getFiltersStateParser().withDefault([]), + hullJoinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"), search: parseAsString.withDefault(""), }) @@ -66,7 +72,7 @@ export const offshoreHullSearchParamsCache = createSearchParamsCache({ // 조선 아이템 업데이트 스키마 export const updateShipbuildingItemSchema = z.object({ itemCode: z.string(), - workType: z.string().optional(), + workType: z.enum(["기장", "전장", "선실", "배관", "철의", "선체"]).optional(), shipTypes: z.string().optional(), itemList: z.string().optional(), }) @@ -80,7 +86,7 @@ export type UpdateShipbuildingItemSchema = z.infer