From 1dc24d48e52f2e490f5603ceb02842586ecae533 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 24 Jul 2025 11:06:32 +0000 Subject: (대표님) 정기평가 피드백 반영, 설계 피드백 반영, (최겸) 기술영업 피드백 반영 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/items-tech/service.ts | 42 ++++++++++++---- lib/items-tech/table/add-items-dialog.tsx | 38 +++++++++------ .../table/hull/offshore-hull-table-columns.tsx | 2 +- lib/items-tech/table/top/import-item-handler.tsx | 4 +- .../table/top/offshore-top-table-columns.tsx | 2 +- .../top/offshore-top-table-toolbar-actions.tsx | 2 +- lib/items-tech/table/top/offshore-top-table.tsx | 3 +- lib/items-tech/table/update-items-sheet.tsx | 56 ++++++++++++++++++---- lib/items-tech/validations.ts | 6 +-- 9 files changed, 113 insertions(+), 42 deletions(-) (limited to 'lib/items-tech') diff --git a/lib/items-tech/service.ts b/lib/items-tech/service.ts index d93c5f96..e0896144 100644 --- a/lib/items-tech/service.ts +++ b/lib/items-tech/service.ts @@ -14,7 +14,7 @@ import { itemShipbuilding, itemOffshoreTop, itemOffshoreHull } from "@/db/schema // 타입 정의 추가 export type ShipbuildingWorkType = '기장' | '전장' | '선실' | '배관' | '철의' | '선체'; -export type OffshoreTopWorkType = 'TM' | 'TS' | 'TE' | 'TP'; +export type OffshoreTopWorkType = 'TM' | 'TS' | 'TE' | 'TP' | 'TA'; export type OffshoreHullWorkType = 'HA' | 'HE' | 'HH' | 'HM' | 'HO' | 'HP' | 'NC'; export interface ShipbuildingItem { @@ -415,6 +415,27 @@ export async function createShipbuildingItem(input: TypedItemCreateData) { } const shipData = input as ShipbuildingItemCreateData; + + // 아이템코드 + 선종 조합으로 중복 체크 + if (input.itemCode && shipData.shipTypes) { + const existingItem = await db.select().from(itemShipbuilding) + .where( + and( + eq(itemShipbuilding.itemCode, input.itemCode), + eq(itemShipbuilding.shipTypes, shipData.shipTypes) + ) + ); + + if (existingItem.length > 0) { + return { + success: false, + message: "중복된 아이템코드 및 선종입니다", + data: null, + error: "중복 키 오류" + } + } + } + const result = await db.insert(itemShipbuilding).values({ itemCode: input.itemCode || "", workType: shipData.workType ? (shipData.workType as '기장' | '전장' | '선실' | '배관' | '철의' | '선체') : '기장', @@ -437,7 +458,7 @@ export async function createShipbuildingItem(input: TypedItemCreateData) { if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, - message: "이미 존재하는 아이템 코드입니다", + message: "중복된 아이템코드 및 선종입니다", data: null, error: "중복 키 오류" } @@ -488,7 +509,7 @@ export async function createShipbuildingImportItem(input: { if (existingItem.length > 0) { return { success: false, - message: "이미 존재하는 아이템 코드 및 선종입니다", + message: "중복된 아이템코드 및 선종입니다", data: null, error: "중복 키 오류" } @@ -517,7 +538,7 @@ export async function createShipbuildingImportItem(input: { if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, - message: "이미 존재하는 아이템 코드 및 선종입니다", + message: "중복된 아이템코드 및 선종입니다", data: null, error: "중복 키 오류" } @@ -557,7 +578,7 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) { if (existingItem.length > 0) { return { success: false, - message: "이미 존재하는 아이템 코드입니다", + message: "중복된 아이템 코드입니다", data: null, error: "중복 키 오류" }; @@ -586,7 +607,7 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) { if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, - message: "이미 존재하는 아이템 코드입니다", + message: "중복된 아이템 코드입니다", data: null, error: "중복 키 오류" }; @@ -626,7 +647,7 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) { if (existingItem.length > 0) { return { success: false, - message: "이미 존재하는 아이템 코드입니다", + message: "중복된 아이템 코드입니다", data: null, error: "중복 키 오류" }; @@ -655,7 +676,7 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) { if (err instanceof Error && err.message.includes("unique constraint")) { return { success: false, - message: "이미 존재하는 아이템 코드입니다", + message: "중복된 아이템 코드입니다", data: null, error: "중복 키 오류" }; @@ -735,7 +756,7 @@ export async function modifyOffshoreTopItem(input: UpdateOffshoreTopItemInput) { try { const updateData: Record = {}; - if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP'; + if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP' | 'TA'; if (input.itemList !== undefined) updateData.itemList = input.itemList; if (input.subItemList !== undefined) updateData.subItemList = input.subItemList; if (input.itemCode) updateData.itemCode = input.itemCode; @@ -781,7 +802,7 @@ export async function modifyOffshoreHullItem(input: UpdateOffshoreHullItemInput) try { const updateData: Record = {}; - if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'NC'; + if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'HO' | 'HP' | 'NC'; if (input.itemList !== undefined) updateData.itemList = input.itemList; if (input.subItemList !== undefined) updateData.subItemList = input.subItemList; if (input.itemCode) updateData.itemCode = input.itemCode; @@ -1222,6 +1243,7 @@ export async function getOffshoreTopWorkTypes() { { code: 'TS' as OffshoreTopWorkType, name: 'TS'}, { code: 'TE' as OffshoreTopWorkType, name: 'TE'}, { code: 'TP' as OffshoreTopWorkType, name: 'TP'}, + { code: 'TA' as OffshoreTopWorkType, name: 'TA'}, ] } diff --git a/lib/items-tech/table/add-items-dialog.tsx b/lib/items-tech/table/add-items-dialog.tsx index 1b0d00c7..01a072da 100644 --- a/lib/items-tech/table/add-items-dialog.tsx +++ b/lib/items-tech/table/add-items-dialog.tsx @@ -53,6 +53,7 @@ const offshoreTopWorkTypes = [ { label: "TS", value: "TS" }, { label: "TE", value: "TE" }, { label: "TP", value: "TP" }, + { label: "TA", value: "TA" }, ] as const // 해양 HULL 공종 유형 정의 @@ -68,7 +69,7 @@ const offshoreHullWorkTypes = [ // 기본 아이템 스키마 const itemFormSchema = z.object({ - itemCode: z.string().optional(), + itemCode: z.string(), workType: z.string().min(1, "공종은 필수입니다"), // 조선 및 해양 아이템 공통 필드 itemList: z.string().optional(), @@ -126,6 +127,8 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { const onSubmit = async (data: ItemFormValues) => { startAddTransition(async () => { try { + let result; + switch (itemType) { case 'shipbuilding': if (!data.shipTypes) { @@ -133,7 +136,7 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { return } - await createShipbuildingItem({ + result = await createShipbuildingItem({ itemCode: data.itemCode || "", workType: data.workType as "기장" | "전장" | "선실" | "배관" | "철의" | "선체", shipTypes: data.shipTypes, @@ -142,16 +145,16 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { break; case 'offshoreTop': - await createOffshoreTopItem({ + result = await createOffshoreTopItem({ itemCode: data.itemCode || "", - workType: data.workType as "TM" | "TS" | "TE" | "TP", + workType: data.workType as "TM" | "TS" | "TE" | "TP" | "TA", itemList: data.itemList || null, subItemList: data.subItemList || null }); break; case 'offshoreHull': - await createOffshoreHullItem({ + result = await createOffshoreHullItem({ itemCode: data.itemCode || "", workType: data.workType as "HA" | "HE" | "HH" | "HM" | "HO" | "HP" | "NC", itemList: data.itemList || null, @@ -164,10 +167,15 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { return; } - toast.success("아이템이 성공적으로 추가되었습니다") - setOpen(false) - form.reset(getDefaultValues()) - router.refresh() + // 결과 확인하여 성공/실패에 따라 다른 메시지 표시 + if (result?.success) { + toast.success("아이템이 성공적으로 추가되었습니다") + setOpen(false) + form.reset(getDefaultValues()) + router.refresh() + } else { + toast.error(result?.message || "아이템 추가에 실패했습니다") + } } catch (error) { toast.error("아이템 추가 중 오류가 발생했습니다") console.error(error) @@ -225,7 +233,9 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { name="itemCode" render={({ field }) => ( - 아이템 코드 + + 자재 그룹 * + @@ -238,7 +248,7 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { name="workType" render={({ field }) => ( - 공종 + 공종 * @@ -294,7 +304,7 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { name="itemList" render={({ field }) => ( - 아이템 리스트 + 자재명 @@ -307,7 +317,7 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) { name="subItemList" render={({ field }) => ( - 서브 아이템 리스트 + 자재명(상세) 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 efc6c583..1ad9035c 100644 --- a/lib/items-tech/table/hull/offshore-hull-table-columns.tsx +++ b/lib/items-tech/table/hull/offshore-hull-table-columns.tsx @@ -22,7 +22,7 @@ import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table- interface OffshoreHullTableItem { id: number; itemId: number; - workType: "HA" | "HE" | "HH" | "HM" | "NC"; + workType: "HA" | "HE" | "HH" | "HM" | "HO" | "HP" | "NC"; itemList: string | null; subItemList: string | null; itemCode: string; diff --git a/lib/items-tech/table/top/import-item-handler.tsx b/lib/items-tech/table/top/import-item-handler.tsx index 4f34cff2..0197d826 100644 --- a/lib/items-tech/table/top/import-item-handler.tsx +++ b/lib/items-tech/table/top/import-item-handler.tsx @@ -4,7 +4,7 @@ import { z } from "zod" import { createOffshoreTopItem } from "../../service" // 해양 TOP 기능(공종) 유형 enum -const TOP_WORK_TYPES = ["TM", "TS", "TE", "TP"] as const; +const TOP_WORK_TYPES = ["TM", "TS", "TE", "TP", "TA"] as const; // 아이템 데이터 검증을 위한 Zod 스키마 const itemSchema = z.object({ @@ -92,7 +92,7 @@ export async function processTopFileImport( // 해양 TOP 아이템 생성 const result = await createOffshoreTopItem({ itemCode: cleanedRow.itemCode, - workType: cleanedRow.workType as "TM" | "TS" | "TE" | "TP", + workType: cleanedRow.workType as "TM" | "TS" | "TE" | "TP" | "TA", itemList: cleanedRow.itemList, subItemList: cleanedRow.subItemList, }); 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 93f27492..e1572e0c 100644 --- a/lib/items-tech/table/top/offshore-top-table-columns.tsx +++ b/lib/items-tech/table/top/offshore-top-table-columns.tsx @@ -22,7 +22,7 @@ import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table- interface OffshoreTopTableItem { id: number; itemId: number; - workType: "TM" | "TS" | "TE" | "TP"; + workType: "TM" | "TS" | "TE" | "TP" | "TA"; itemList: string | null; subItemList: string | null; itemCode: string; 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 bf10560f..c81feda0 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 @@ -23,7 +23,7 @@ import { ImportItemButton } from "../import-excel-button" interface OffshoreTopItem { id: number; itemId: number; - workType: "TM" | "TS" | "TE" | "TP"; + workType: "TM" | "TS" | "TE" | "TP" | "TA"; itemList: string | null; subItemList: string | null; itemCode: string; diff --git a/lib/items-tech/table/top/offshore-top-table.tsx b/lib/items-tech/table/top/offshore-top-table.tsx index c038de13..dc76a06a 100644 --- a/lib/items-tech/table/top/offshore-top-table.tsx +++ b/lib/items-tech/table/top/offshore-top-table.tsx @@ -20,7 +20,7 @@ import { UpdateItemSheet } from "../update-items-sheet" type OffshoreTopItem = { id: number; itemId: number; - workType: "TM" | "TS" | "TE" | "TP"; + workType: "TM" | "TS" | "TE" | "TP" | "TA"; itemList: string | null; subItemList: string | null; itemCode: string; @@ -74,6 +74,7 @@ export function OffshoreTopTable({ promises }: OffshoreTopTableProps) { { label: "TS", value: "TS" }, { label: "TE", value: "TE" }, { label: "TP", value: "TP" }, + { label: "TA", value: "TA" }, ], }, { diff --git a/lib/items-tech/table/update-items-sheet.tsx b/lib/items-tech/table/update-items-sheet.tsx index 978e83d5..91108ba0 100644 --- a/lib/items-tech/table/update-items-sheet.tsx +++ b/lib/items-tech/table/update-items-sheet.tsx @@ -52,6 +52,7 @@ const offshoreTopWorkTypes = [ { value: "TS", label: "TS" }, { value: "TE", label: "TE" }, { value: "TP", label: "TP" }, + { value: "TA", label: "TA" }, ] as const const offshoreHullWorkTypes = [ @@ -76,7 +77,7 @@ type ShipbuildingItem = { type OffshoreTopItem = { id: number itemCode: string - workType: "TM" | "TS" | "TE" | "TP" + workType: "TM" | "TS" | "TE" | "TP" | "TA" itemList: string | null subItemList: string | null } @@ -94,6 +95,7 @@ type UpdateItemSchema = { workType?: string shipTypes?: string itemList?: string + subItemList?: string } interface UpdateItemSheetProps { @@ -125,11 +127,16 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt itemList: (item as ShipbuildingItem).itemList || "", }; case 'offshoreTop': + const offshoreTopItem = item as OffshoreTopItem; + return { + itemList: offshoreTopItem.itemList || "", + subItemList: offshoreTopItem.subItemList || "" + }; case 'offshoreHull': - const offshoreItem = item as OffshoreTopItem | OffshoreHullItem; + const offshoreHullItem = item as OffshoreHullItem; return { - itemList: offshoreItem.itemList || "", - subItemList: offshoreItem.subItemList || "" + itemList: offshoreHullItem.itemList || "", + subItemList: offshoreHullItem.subItemList || "" }; default: return {}; @@ -224,7 +231,7 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
@@ -235,7 +242,7 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt name="workType" render={({ field }) => ( - 기능(공종) + 기능(공종) * @@ -281,14 +288,45 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt name="itemList" render={({ field }) => ( - 아이템 리스트 + 자재명 - + )} /> + {itemType === 'offshoreHull' && ( + ( + + 자재명(상세) + + + + + + )} + /> + )} + {itemType === 'offshoreTop' && ( + ( + + 자재명(상세) + + + + + + )} + /> + )} + diff --git a/lib/items-tech/validations.ts b/lib/items-tech/validations.ts index 95a34b58..ec662320 100644 --- a/lib/items-tech/validations.ts +++ b/lib/items-tech/validations.ts @@ -107,7 +107,7 @@ export type TypedItemCreateData = ShipbuildingItemCreateData // 해양 TOP 아이템 스키마 export const createOffshoreTopItemSchema = z.object({ itemCode: z.string(), - workType: z.enum(["TM", "TS", "TE", "TP"]), + workType: z.enum(["TM", "TS", "TE", "TP", "TA"]), itemList: z.string().optional(), subItemList: z.string().optional(), }) @@ -126,7 +126,7 @@ export type CreateOffshoreHullItemSchema = z.infer