summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-05-20 09:01:22 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-05-20 09:01:22 +0000
commit45f4c426c98d86a251644a4858740bec989edf83 (patch)
treeb0a3f1ce6ac3e4493ee53c93ef33841c8eb34cfb
parent11f13979825d28180956fc27600176bfc47457e1 (diff)
(최겸) 기술영업 아이템리스트 수정 및 개발 0520
-rw-r--r--app/[lng]/evcp/(evcp)/items-tech/layout.tsx13
-rw-r--r--app/[lng]/evcp/(evcp)/items-tech/page.tsx24
-rw-r--r--db/schema/items.ts15
-rw-r--r--lib/items-tech/repository.ts6
-rw-r--r--lib/items-tech/service.ts317
-rw-r--r--lib/items-tech/table/add-items-dialog.tsx125
-rw-r--r--lib/items-tech/table/hull/import-item-handler.tsx36
-rw-r--r--lib/items-tech/table/hull/item-excel-template.tsx32
-rw-r--r--lib/items-tech/table/hull/offshore-hull-table-columns.tsx116
-rw-r--r--lib/items-tech/table/hull/offshore-hull-table-toolbar-actions.tsx12
-rw-r--r--lib/items-tech/table/hull/offshore-hull-table.tsx32
-rw-r--r--lib/items-tech/table/ship/Items-ship-table.tsx10
-rw-r--r--lib/items-tech/table/ship/import-item-handler.tsx56
-rw-r--r--lib/items-tech/table/ship/item-excel-template.tsx27
-rw-r--r--lib/items-tech/table/ship/items-ship-table-columns.tsx73
-rw-r--r--lib/items-tech/table/ship/items-table-toolbar-actions.tsx4
-rw-r--r--lib/items-tech/table/top/import-item-handler.tsx36
-rw-r--r--lib/items-tech/table/top/item-excel-template.tsx33
-rw-r--r--lib/items-tech/table/top/offshore-top-table-columns.tsx116
-rw-r--r--lib/items-tech/table/top/offshore-top-table-toolbar-actions.tsx12
-rw-r--r--lib/items-tech/table/top/offshore-top-table.tsx29
-rw-r--r--lib/items-tech/table/update-items-sheet.tsx140
-rw-r--r--lib/items-tech/validations.ts39
23 files changed, 401 insertions, 902 deletions
diff --git a/app/[lng]/evcp/(evcp)/items-tech/layout.tsx b/app/[lng]/evcp/(evcp)/items-tech/layout.tsx
index 5eb88325..d375059b 100644
--- a/app/[lng]/evcp/(evcp)/items-tech/layout.tsx
+++ b/app/[lng]/evcp/(evcp)/items-tech/layout.tsx
@@ -1,6 +1,7 @@
import * as React from "react"
import { ItemTechContainer } from "@/components/items-tech/item-tech-container"
import { Shell } from "@/components/shell"
+import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"
// Layout 컴포넌트는 서버 컴포넌트입니다
export default function ItemsShipLayout({
@@ -17,9 +18,21 @@ export default function ItemsShipLayout({
return (
<Shell className="gap-4">
+ <React.Suspense
+ fallback={
+ <DataTableSkeleton
+ columnCount={6}
+ searchableColumnCount={1}
+ filterableColumnCount={2}
+ cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
+ shrinkZero
+ />
+ }
+ >
<ItemTechContainer itemTypes={itemTypes}>
{children}
</ItemTechContainer>
+ </React.Suspense>
</Shell>
)
}
diff --git a/app/[lng]/evcp/(evcp)/items-tech/page.tsx b/app/[lng]/evcp/(evcp)/items-tech/page.tsx
index 0680f78b..52ff519d 100644
--- a/app/[lng]/evcp/(evcp)/items-tech/page.tsx
+++ b/app/[lng]/evcp/(evcp)/items-tech/page.tsx
@@ -15,26 +15,16 @@ interface IndexPageProps {
searchParams: Promise<SearchParams>
}
-export default async function IndexPage(props: IndexPageProps) {
- const searchParams = await props.searchParams
- const search = searchParamsCache.parse(searchParams)
- const validFilters = getValidFilters(search.filters)
+export default async function IndexPage({ searchParams }: IndexPageProps) {
+ const params = await searchParams
+ const search = searchParamsCache.parse(params)
+ const validFilters = getValidFilters(search.filters || [])
// URL에서 아이템 타입 가져오기
- const itemType = searchParams.type || "ship"
+ const itemType = params.type || "ship"
return (
- <React.Suspense
- fallback={
- <DataTableSkeleton
- columnCount={6}
- searchableColumnCount={1}
- filterableColumnCount={2}
- cellWidths={["10rem", "40rem", "12rem", "12rem", "8rem", "8rem"]}
- shrinkZero
- />
- }
- >
+ <div>
{itemType === "ship" && (
<ItemsShipTable
promises={Promise.all([
@@ -67,6 +57,6 @@ export default async function IndexPage(props: IndexPageProps) {
]).then(([result]) => result)}
/>
)}
- </React.Suspense>
+ </div>
)
}
diff --git a/db/schema/items.ts b/db/schema/items.ts
index 4218f0f4..a032de04 100644
--- a/db/schema/items.ts
+++ b/db/schema/items.ts
@@ -31,7 +31,8 @@ export const itemShipbuilding = pgTable("item_shipbuilding", {
id: serial("id").primaryKey(),
itemId: integer("item_id").notNull().references(() => items.id, { onDelete: 'cascade' }),
workType: workTypeEnum("work_type").notNull(),
- shipTypes: text("ship_types").notNull().default('A-MAX'),
+ itemList: text("item_list"),
+ shipTypes: text("ship_types").notNull().default('OPTION'),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
@@ -55,10 +56,8 @@ export const itemOffshoreTop = pgTable("item_offshore_top", {
id: serial("id").primaryKey(),
itemId: integer("item_id").notNull().references(() => items.id, { onDelete: 'cascade' }),
workType: offshoreTopWorkTypeEnum("work_type").notNull(),
- itemList1: text("item_list1"),
- itemList2: text("item_list2"),
- itemList3: text("item_list3"),
- itemList4: text("item_list4"),
+ itemList: text("item_list"),
+ subItemList: text("sub_item_list"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
@@ -68,10 +67,8 @@ export const itemOffshoreHull = pgTable("item_offshore_hull", {
id: serial("id").primaryKey(),
itemId: integer("item_id").notNull().references(() => items.id, { onDelete: 'cascade' }),
workType: offshoreHullWorkTypeEnum("work_type").notNull(),
- itemList1: text("item_list1"),
- itemList2: text("item_list2"),
- itemList3: text("item_list3"),
- itemList4: text("item_list4"),
+ itemList: text("item_list"),
+ subItemList: text("sub_item_list"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
diff --git a/lib/items-tech/repository.ts b/lib/items-tech/repository.ts
index 550e6b1d..43e88866 100644
--- a/lib/items-tech/repository.ts
+++ b/lib/items-tech/repository.ts
@@ -4,15 +4,9 @@ import { Item, items } from "@/db/schema/items";
import {
eq,
inArray,
- not,
asc,
desc,
- and,
- ilike,
- gte,
- lte,
count,
- gt,
} from "drizzle-orm";
import { PgTransaction } from "drizzle-orm/pg-core";
export type NewItem = typeof items.$inferInsert
diff --git a/lib/items-tech/service.ts b/lib/items-tech/service.ts
index 97aacfba..02a5a901 100644
--- a/lib/items-tech/service.ts
+++ b/lib/items-tech/service.ts
@@ -11,7 +11,7 @@ import { getErrorMessage } from "@/lib/handle-error";
import { asc, desc, ilike, and, or, eq, count, inArray, sql } from "drizzle-orm";
import { GetItemsSchema, UpdateItemSchema, ShipbuildingItemCreateData, TypedItemCreateData, OffshoreTopItemCreateData, OffshoreHullItemCreateData } from "./validations";
import { Item, items, itemShipbuilding, itemOffshoreTop, itemOffshoreHull } from "@/db/schema/items";
-import { findAllItems, insertItem, updateItem } from "./repository";
+import { findAllItems } from "./repository";
/* -----------------------------------------------------
1) 조회 관련
@@ -65,6 +65,7 @@ export async function getShipbuildingItems(input: GetItemsSchema) {
itemId: itemShipbuilding.itemId,
workType: itemShipbuilding.workType,
shipTypes: itemShipbuilding.shipTypes,
+ itemList: itemShipbuilding.itemList,
itemCode: items.itemCode,
itemName: items.itemName,
description: items.description,
@@ -144,10 +145,8 @@ export async function getOffshoreTopItems(input: GetItemsSchema) {
id: itemOffshoreTop.id,
itemId: itemOffshoreTop.itemId,
workType: itemOffshoreTop.workType,
- itemList1: itemOffshoreTop.itemList1,
- itemList2: itemOffshoreTop.itemList2,
- itemList3: itemOffshoreTop.itemList3,
- itemList4: itemOffshoreTop.itemList4,
+ itemList: itemOffshoreTop.itemList,
+ subItemList: itemOffshoreTop.subItemList,
itemCode: items.itemCode,
itemName: items.itemName,
description: items.description,
@@ -227,10 +226,8 @@ export async function getOffshoreHullItems(input: GetItemsSchema) {
id: itemOffshoreHull.id,
itemId: itemOffshoreHull.itemId,
workType: itemOffshoreHull.workType,
- itemList1: itemOffshoreHull.itemList1,
- itemList2: itemOffshoreHull.itemList2,
- itemList3: itemOffshoreHull.itemList3,
- itemList4: itemOffshoreHull.itemList4,
+ itemList: itemOffshoreHull.itemList,
+ subItemList: itemOffshoreHull.subItemList,
itemCode: items.itemCode,
itemName: items.itemName,
description: items.description,
@@ -279,14 +276,20 @@ export async function createShipbuildingItem(input: TypedItemCreateData) {
unstable_noStore()
try {
- if (!input.itemCode || !input.itemName) {
+ if (!input.itemCode) {
return {
success: false,
- message: "아이템 코드와 아이템 명은 필수입니다",
+ message: "아이템 코드는 필수입니다",
data: null,
error: "필수 필드 누락"
}
}
+
+ // itemName이 없으면 "기술영업"으로 설정
+ if (!input.itemName) {
+ input.itemName = "기술영업"
+ }
+
const result = await db.transaction(async (tx) => {
// 1. itemCode 정규화해서 직접 쿼리
const existRows = await tx.select().from(items)
@@ -294,15 +297,12 @@ export async function createShipbuildingItem(input: TypedItemCreateData) {
const existingItem = existRows[0];
let itemId: number;
- let itemResult;
+ let itemResult: any;
if (existingItem) {
- // 이미 있으면 업데이트
- itemResult = await updateItem(tx, existingItem.id, {
- itemName: input.itemName,
- description: input.description,
- });
+ // 기존 아이템이 있으면 업데이트하지 않고 그대로 사용
itemId = existingItem.id;
+ itemResult = [existingItem]; // 배열 형태로 반환
} else {
// 없으면 새로 생성
// 현재 가장 큰 ID 값 가져오기
@@ -326,6 +326,7 @@ export async function createShipbuildingItem(input: TypedItemCreateData) {
itemId: itemId,
workType: shipData.workType ? (shipData.workType as '기장' | '전장' | '선실' | '배관' | '철의') : '기장',
shipTypes: shipData.shipTypes || '',
+ itemList: shipData.itemList || null,
createdAt: new Date(),
updatedAt: new Date()
}).returning();
@@ -370,20 +371,27 @@ export async function createShipbuildingImportItem(input: {
itemName: string;
workType: '기장' | '전장' | '선실' | '배관' | '철의';
description?: string | null;
+ itemList?: string | null;
shipTypes: Record<string, boolean>;
}) {
unstable_noStore();
try {
- if (!input.itemCode || !input.itemName) {
+ if (!input.itemCode) {
return {
success: false,
- message: "아이템 코드와 아이템 명은 필수입니다",
+ message: "아이템 코드는 필수입니다",
data: null,
error: "필수 필드 누락"
}
}
+
+ // itemName이 없을 경우 "기술영업"으로 설정
+ if (!input.itemName) {
+ input.itemName = "기술영업";
+ }
+
const results = await db.transaction(async (tx) => {
// 1. itemCode 정규화해서 직접 쿼리
const existRows = await tx.select().from(items)
@@ -395,13 +403,9 @@ export async function createShipbuildingImportItem(input: {
let itemId: number;
if (existingItem) {
- // 이미 있으면 업데이트
- await updateItem(tx, existingItem.id, {
- itemName: input.itemName,
- description: input.description,
- });
+ // 기존 아이템이 있으면 업데이트하지 않고 그대로 사용
itemId = existingItem.id;
- console.log('기존 아이템 업데이트, id:', itemId);
+ console.log('기존 아이템 사용, id:', itemId);
} else {
// 없으면 새로 생성
// 현재 가장 큰 ID 값 가져오기
@@ -437,6 +441,7 @@ export async function createShipbuildingImportItem(input: {
itemId: itemId,
workType: input.workType,
shipTypes: shipType,
+ itemList: input.itemList || null,
createdAt: new Date(),
updatedAt: new Date()
}).returning();
@@ -473,14 +478,19 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) {
unstable_noStore()
try {
- if (!data.itemCode || !data.itemName) {
+ if (!data.itemCode) {
return {
success: false,
- message: "아이템 코드와 아이템 명은 필수입니다",
+ message: "아이템 코드는 필수입니다",
data: null,
error: "필수 필드 누락"
}
}
+
+ // itemName이 없으면 "기술영업"으로 설정
+ if (!data.itemName) {
+ data.itemName = "기술영업"
+ }
// 트랜잭션 내에서 처리
const result = await db.transaction(async (tx) => {
@@ -490,15 +500,12 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) {
const existingItem = existRows[0];
let itemId: number;
- let itemResult;
+ let itemResult: any;
if (existingItem) {
- // 이미 있으면 업데이트
- itemResult = await updateItem(tx, existingItem.id, {
- itemName: data.itemName,
- description: data.description,
- });
+ // 기존 아이템이 있으면 업데이트하지 않고 그대로 사용
itemId = existingItem.id;
+ itemResult = [existingItem]; // 배열 형태로 반환
} else {
// 없으면 새로 생성
// 현재 가장 큰 ID 값 가져오기
@@ -522,10 +529,8 @@ export async function createOffshoreTopItem(data: OffshoreTopItemCreateData) {
.values({
itemId: itemId,
workType: data.workType,
- itemList1: data.itemList1 || null,
- itemList2: data.itemList2 || null,
- itemList3: data.itemList3 || null,
- itemList4: data.itemList4 || null,
+ itemList: data.itemList,
+ subItemList: data.subItemList,
createdAt: new Date(),
updatedAt: new Date()
})
@@ -566,14 +571,19 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) {
unstable_noStore()
try {
- if (!data.itemCode || !data.itemName) {
+ if (!data.itemCode) {
return {
success: false,
- message: "아이템 코드와 아이템 명은 필수입니다",
+ message: "아이템 코드는 필수입니다",
data: null,
error: "필수 필드 누락"
}
}
+
+ // itemName이 없으면 "기술영업"으로 설정
+ if (!data.itemName) {
+ data.itemName = "기술영업"
+ }
// 트랜잭션 내에서 처리
const result = await db.transaction(async (tx) => {
@@ -583,15 +593,12 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) {
const existingItem = existRows[0];
let itemId: number;
- let itemResult;
+ let itemResult: any;
if (existingItem) {
- // 이미 있으면 업데이트
- itemResult = await updateItem(tx, existingItem.id, {
- itemName: data.itemName,
- description: data.description,
- });
+ // 기존 아이템이 있으면 업데이트하지 않고 그대로 사용
itemId = existingItem.id;
+ itemResult = [existingItem]; // 배열 형태로 반환
} else {
// 없으면 새로 생성
// 현재 가장 큰 ID 값 가져오기
@@ -615,10 +622,8 @@ export async function createOffshoreHullItem(data: OffshoreHullItemCreateData) {
.values({
itemId: itemId,
workType: data.workType,
- itemList1: data.itemList1 || null,
- itemList2: data.itemList2 || null,
- itemList3: data.itemList3 || null,
- itemList4: data.itemList4 || null,
+ itemList: data.itemList,
+ subItemList: data.subItemList,
createdAt: new Date(),
updatedAt: new Date()
})
@@ -664,6 +669,7 @@ interface UpdateShipbuildingItemInput extends UpdateItemSchema {
id: number;
workType?: string;
shipTypes?: string;
+ itemList?: string;
itemCode?: string;
itemName?: string;
description?: string;
@@ -686,58 +692,25 @@ export async function modifyShipbuildingItem(input: UpdateShipbuildingItemInput)
throw new Error("아이템을 찾을 수 없습니다.");
}
- const existingItem = existingShipbuilding.item;
+ // 세부 아이템 테이블만 업데이트 (items 테이블은 변경하지 않음)
+ const updateData: Record<string, unknown> = {};
- // 아이템 테이블 정보가 변경되었는지 확인
- const isItemChanged =
- (input.itemCode && input.itemCode !== existingItem.itemCode) ||
- (input.itemName && input.itemName !== existingItem.itemName) ||
- (input.description !== undefined && input.description !== existingItem.description);
+ if (input.workType) updateData.workType = input.workType as '기장' | '전장' | '선실' | '배관' | '철의';
+ if (input.shipTypes) updateData.shipTypes = input.shipTypes;
+ if (input.itemList !== undefined) updateData.itemList = input.itemList;
- // 세부 아이템 정보만 변경된 경우
- if (!isItemChanged) {
- // 조선 아이템 테이블 업데이트
- if (input.workType || input.shipTypes) {
- await tx.update(itemShipbuilding)
- .set({
- workType: input.workType as '기장' | '전장' | '선실' | '배관' | '철의',
- shipTypes: input.shipTypes
- })
- .where(eq(itemShipbuilding.id, input.id));
- }
-
- return {
- data: { id: input.id },
- error: null,
- success: true,
- message: "아이템이 성공적으로 업데이트되었습니다."
- };
- }
- // 아이템 테이블 정보가 변경된 경우 - 새 아이템 생성하고 세부 아이템 연결
- else {
- // 새 아이템 생성
- const [newItem] = await insertItem(tx, {
- itemCode: input.itemCode || existingItem.itemCode,
- itemName: input.itemName || existingItem.itemName,
- description: input.description !== undefined ? input.description : existingItem.description,
- });
-
- // 세부 아이템 테이블 정보를 새 아이템에 연결
+ if (Object.keys(updateData).length > 0) {
await tx.update(itemShipbuilding)
- .set({
- itemId: newItem.id,
- workType: input.workType ? (input.workType as '기장' | '전장' | '선실' | '배관' | '철의') : existingShipbuilding.workType,
- shipTypes: input.shipTypes || existingShipbuilding.shipTypes
- })
+ .set(updateData)
.where(eq(itemShipbuilding.id, input.id));
-
- return {
- data: { id: input.id },
- error: null,
- success: true,
- message: "새 아이템이 생성되고 세부 정보가 업데이트되었습니다."
- };
}
+
+ return {
+ data: { id: input.id },
+ error: null,
+ success: true,
+ message: "아이템이 성공적으로 업데이트되었습니다."
+ };
});
// 캐시 무효화
@@ -758,10 +731,8 @@ export async function modifyShipbuildingItem(input: UpdateShipbuildingItemInput)
interface UpdateOffshoreTopItemInput extends UpdateItemSchema {
id: number;
workType?: string;
- itemList1?: string;
- itemList2?: string;
- itemList3?: string;
- itemList4?: string;
+ itemList?: string;
+ subItemList?: string;
itemCode?: string;
itemName?: string;
description?: string;
@@ -784,69 +755,25 @@ export async function modifyOffshoreTopItem(input: UpdateOffshoreTopItemInput) {
throw new Error("아이템을 찾을 수 없습니다.");
}
- const existingItem = existingOffshoreTop.item;
+ // 세부 아이템 테이블만 업데이트 (items 테이블은 변경하지 않음)
+ const updateData: Record<string, unknown> = {};
- // 아이템 테이블 정보가 변경되었는지 확인
- const isItemChanged =
- (input.itemCode && input.itemCode !== existingItem.itemCode) ||
- (input.itemName && input.itemName !== existingItem.itemName) ||
- (input.description !== undefined && input.description !== existingItem.description);
+ if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP';
+ if (input.itemList !== undefined) updateData.itemList = input.itemList;
+ if (input.subItemList !== undefined) updateData.subItemList = input.subItemList;
- // 세부 아이템 정보만 변경된 경우
- if (!isItemChanged) {
- // Offshore TOP 아이템 테이블 업데이트
- const updateData: Record<string, unknown> = {};
-
- if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP';
- if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1;
- if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2;
- if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3;
- if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4;
-
- if (Object.keys(updateData).length > 0) {
- await tx.update(itemOffshoreTop)
- .set(updateData)
- .where(eq(itemOffshoreTop.id, input.id));
- }
-
- return {
- data: { id: input.id },
- error: null,
- success: true,
- message: "아이템이 성공적으로 업데이트되었습니다."
- };
- }
- // 아이템 테이블 정보가 변경된 경우 - 새 아이템 생성하고 세부 아이템 연결
- else {
- // 새 아이템 생성
- const [newItem] = await insertItem(tx, {
- itemCode: input.itemCode || existingItem.itemCode,
- itemName: input.itemName || existingItem.itemName,
- description: input.description !== undefined ? input.description : existingItem.description,
- });
-
- // 세부 아이템 테이블 정보를 새 아이템에 연결
- const updateData: Record<string, unknown> = {
- itemId: newItem.id
- };
-
- if (input.workType) updateData.workType = input.workType as 'TM' | 'TS' | 'TE' | 'TP';
- if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1;
- if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2;
- if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3;
- if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4;
-
+ if (Object.keys(updateData).length > 0) {
await tx.update(itemOffshoreTop)
.set(updateData)
.where(eq(itemOffshoreTop.id, input.id));
-
- return {
- data: { id: input.id },
- error: null,
- success: true,
- message: "새 아이템이 생성되고 세부 정보가 업데이트되었습니다."
- };
}
+
+ return {
+ data: { id: input.id },
+ error: null,
+ success: true,
+ message: "아이템이 성공적으로 업데이트되었습니다."
+ };
});
// 캐시 무효화
@@ -867,10 +794,8 @@ export async function modifyOffshoreTopItem(input: UpdateOffshoreTopItemInput) {
interface UpdateOffshoreHullItemInput extends UpdateItemSchema {
id: number;
workType?: string;
- itemList1?: string;
- itemList2?: string;
- itemList3?: string;
- itemList4?: string;
+ itemList?: string;
+ subItemList?: string;
itemCode?: string;
itemName?: string;
description?: string;
@@ -893,69 +818,25 @@ export async function modifyOffshoreHullItem(input: UpdateOffshoreHullItemInput)
throw new Error("아이템을 찾을 수 없습니다.");
}
- const existingItem = existingOffshoreHull.item;
+ // 세부 아이템 테이블만 업데이트 (items 테이블은 변경하지 않음)
+ const updateData: Record<string, unknown> = {};
- // 아이템 테이블 정보가 변경되었는지 확인
- const isItemChanged =
- (input.itemCode && input.itemCode !== existingItem.itemCode) ||
- (input.itemName && input.itemName !== existingItem.itemName) ||
- (input.description !== undefined && input.description !== existingItem.description);
+ if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'NC';
+ if (input.itemList !== undefined) updateData.itemList = input.itemList;
+ if (input.subItemList !== undefined) updateData.subItemList = input.subItemList;
- // 세부 아이템 정보만 변경된 경우
- if (!isItemChanged) {
- // Offshore HULL 아이템 테이블 업데이트
- const updateData: Record<string, unknown> = {};
-
- if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'NC';
- if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1;
- if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2;
- if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3;
- if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4;
-
- if (Object.keys(updateData).length > 0) {
- await tx.update(itemOffshoreHull)
- .set(updateData)
- .where(eq(itemOffshoreHull.id, input.id));
- }
-
- return {
- data: { id: input.id },
- error: null,
- success: true,
- message: "아이템이 성공적으로 업데이트되었습니다."
- };
- }
- // 아이템 테이블 정보가 변경된 경우 - 새 아이템 생성하고 세부 아이템 연결
- else {
- // 새 아이템 생성
- const [newItem] = await insertItem(tx, {
- itemCode: input.itemCode || existingItem.itemCode,
- itemName: input.itemName || existingItem.itemName,
- description: input.description !== undefined ? input.description : existingItem.description,
- });
-
- // 세부 아이템 테이블 정보를 새 아이템에 연결
- const updateData: Record<string, unknown> = {
- itemId: newItem.id
- };
-
- if (input.workType) updateData.workType = input.workType as 'HA' | 'HE' | 'HH' | 'HM' | 'NC';
- if (input.itemList1 !== undefined) updateData.itemList1 = input.itemList1;
- if (input.itemList2 !== undefined) updateData.itemList2 = input.itemList2;
- if (input.itemList3 !== undefined) updateData.itemList3 = input.itemList3;
- if (input.itemList4 !== undefined) updateData.itemList4 = input.itemList4;
-
+ if (Object.keys(updateData).length > 0) {
await tx.update(itemOffshoreHull)
.set(updateData)
.where(eq(itemOffshoreHull.id, input.id));
-
- return {
- data: { id: input.id },
- error: null,
- success: true,
- message: "새 아이템이 생성되고 세부 정보가 업데이트되었습니다."
- };
}
+
+ return {
+ data: { id: input.id },
+ error: null,
+ success: true,
+ message: "아이템이 성공적으로 업데이트되었습니다."
+ };
});
// 캐시 무효화
diff --git a/lib/items-tech/table/add-items-dialog.tsx b/lib/items-tech/table/add-items-dialog.tsx
index 86333189..a3af0a8c 100644
--- a/lib/items-tech/table/add-items-dialog.tsx
+++ b/lib/items-tech/table/add-items-dialog.tsx
@@ -32,7 +32,6 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
-import { Textarea } from "@/components/ui/textarea"
import { toast } from "sonner"
import { createShipbuildingItem, createOffshoreTopItem, createOffshoreHullItem } from "../service"
@@ -47,14 +46,15 @@ const shipbuildingWorkTypes = [
{ label: "철의", value: "철의" },
] as const
-// 선종 유형 정의
-const shipTypes = [
- { label: "A-MAX", value: "A-MAX" },
- { label: "S-MAX", value: "S-MAX" },
- { label: "LNGC", value: "LNGC" },
- { label: "VLCC", value: "VLCC" },
- { label: "CONT", value: "CONT" },
-] as const
+// // 선종 유형 정의
+// const shipTypes = [
+// { label: "A-MAX", value: "A-MAX" },
+// { label: "S-MAX", value: "S-MAX" },
+// { label: "LNGC", value: "LNGC" },
+// { label: "VLCC", value: "VLCC" },
+// { label: "CONT", value: "CONT" },
+// { label: "OPTION", value: "OPTION" },
+// ] as const
// 해양 TOP 공종 유형 정의
const offshoreTopWorkTypes = [
@@ -79,13 +79,12 @@ const itemFormSchema = z.object({
itemName: z.string().min(1, "아이템 명은 필수입니다"),
description: z.string().optional(),
workType: z.string().min(1, "공종은 필수입니다"),
+ // 조선 및 해양 아이템 공통 필드
+ itemList: z.string().optional(),
// 조선 아이템 전용 필드
shipTypes: z.string().optional(),
// 해양 아이템 전용 필드
- itemList1: z.string().optional(),
- itemList2: z.string().optional(),
- itemList3: z.string().optional(),
- itemList4: z.string().optional(),
+ subItemList: z.string().optional(),
})
type ItemFormValues = z.infer<typeof itemFormSchema>
@@ -102,18 +101,17 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
const getDefaultValues = () => {
const defaults: ItemFormValues = {
itemCode: "",
- itemName: "",
+ itemName: "기술영업",
description: "",
workType: getDefaultWorkType(),
}
if (itemType === 'shipbuilding') {
- defaults.shipTypes = "A-MAX"
+ defaults.shipTypes = "OPTION"
+ defaults.itemList = ""
} else {
- defaults.itemList1 = ""
- defaults.itemList2 = ""
- defaults.itemList3 = ""
- defaults.itemList4 = ""
+ defaults.itemList = ""
+ defaults.subItemList = ""
}
return defaults
@@ -151,7 +149,8 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
itemName: data.itemName,
workType: data.workType,
shipTypes: data.shipTypes,
- description: data.description || null
+ description: data.description || null,
+ itemList: data.itemList || null
});
break;
@@ -161,10 +160,8 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
itemName: data.itemName,
workType: data.workType as "TM" | "TS" | "TE" | "TP",
description: data.description || null,
- itemList1: data.itemList1 || null,
- itemList2: data.itemList2 || null,
- itemList3: data.itemList3 || null,
- itemList4: data.itemList4 || null
+ itemList: data.itemList || null,
+ subItemList: data.subItemList || null
});
break;
@@ -174,10 +171,8 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
itemName: data.itemName,
workType: data.workType as "HA" | "HE" | "HH" | "HM" | "NC",
description: data.description || null,
- itemList1: data.itemList1 || null,
- itemList2: data.itemList2 || null,
- itemList3: data.itemList3 || null,
- itemList4: data.itemList4 || null
+ itemList: data.itemList || null,
+ subItemList: data.subItemList || null
});
break;
@@ -256,19 +251,6 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
/>
<FormField
control={form.control}
- name="itemName"
- render={({ field }) => (
- <FormItem>
- <FormLabel>아이템 명</FormLabel>
- <FormControl>
- <Input {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
name="workType"
render={({ field }) => (
<FormItem>
@@ -292,41 +274,15 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
)}
/>
{itemType === 'shipbuilding' && (
- <FormField
- control={form.control}
- name="shipTypes"
- render={({ field }) => (
- <FormItem>
- <FormLabel>선종</FormLabel>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
- <FormControl>
- <SelectTrigger>
- <SelectValue placeholder="선종을 선택하세요" />
- </SelectTrigger>
- </FormControl>
- <SelectContent>
- {shipTypes.map((type) => (
- <SelectItem key={type.value} value={type.value}>
- {type.label}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- <FormMessage />
- </FormItem>
- )}
- />
- )}
- {(itemType === 'offshoreTop' || itemType === 'offshoreHull') && (
<>
<FormField
control={form.control}
- name="itemList1"
+ name="shipTypes"
render={({ field }) => (
<FormItem>
- <FormLabel>아이템 리스트 1</FormLabel>
+ <FormLabel>선종</FormLabel>
<FormControl>
- <Input {...field} />
+ <Input placeholder="선종을 입력하세요" {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -334,10 +290,10 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
/>
<FormField
control={form.control}
- name="itemList2"
+ name="itemList"
render={({ field }) => (
<FormItem>
- <FormLabel>아이템 리스트 2</FormLabel>
+ <FormLabel>아이템 리스트</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -345,12 +301,16 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
</FormItem>
)}
/>
+ </>
+ )}
+ {(itemType === 'offshoreTop' || itemType === 'offshoreHull') && (
+ <>
<FormField
control={form.control}
- name="itemList3"
+ name="itemList"
render={({ field }) => (
<FormItem>
- <FormLabel>아이템 리스트 3</FormLabel>
+ <FormLabel>아이템 리스트</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -360,10 +320,10 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
/>
<FormField
control={form.control}
- name="itemList4"
+ name="subItemList"
render={({ field }) => (
<FormItem>
- <FormLabel>아이템 리스트 4</FormLabel>
+ <FormLabel>서브 아이템 리스트</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
@@ -373,19 +333,6 @@ export function AddItemDialog({ itemType }: AddItemDialogProps) {
/>
</>
)}
- <FormField
- control={form.control}
- name="description"
- render={({ field }) => (
- <FormItem>
- <FormLabel>설명</FormLabel>
- <FormControl>
- <Textarea {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
<div className="flex justify-end space-x-2">
<Button type="button" variant="outline" onClick={() => setOpen(false)}>
취소
diff --git a/lib/items-tech/table/hull/import-item-handler.tsx b/lib/items-tech/table/hull/import-item-handler.tsx
index 40070769..38a176cb 100644
--- a/lib/items-tech/table/hull/import-item-handler.tsx
+++ b/lib/items-tech/table/hull/import-item-handler.tsx
@@ -9,15 +9,11 @@ const HULL_WORK_TYPES = ["HA", "HE", "HH", "HM", "NC"] as const;
// 아이템 데이터 검증을 위한 Zod 스키마
const itemSchema = z.object({
itemCode: z.string().min(1, "아이템 코드는 필수입니다"),
- itemName: z.string().min(1, "아이템 명은 필수입니다"),
workType: z.enum(HULL_WORK_TYPES, {
required_error: "기능(공종)은 필수입니다",
}),
- description: z.string().nullable().optional(),
- itemList1: z.string().nullable().optional(),
- itemList2: z.string().nullable().optional(),
- itemList3: z.string().nullable().optional(),
- itemList4: z.string().nullable().optional(),
+ itemList: z.string().nullable().optional(),
+ subItemList: z.string().nullable().optional(),
});
interface ProcessResult {
@@ -30,7 +26,7 @@ interface ProcessResult {
* Excel 파일에서 가져온 해양 HULL 아이템 데이터 처리하는 함수
*/
export async function processHullFileImport(
- jsonData: any[],
+ jsonData: Record<string, unknown>[],
progressCallback?: (current: number, total: number) => void
): Promise<ProcessResult> {
// 결과 카운터 초기화
@@ -65,24 +61,16 @@ export async function processHullFileImport(
try {
// 필드 매핑 (한글/영문 필드명 모두 지원)
const itemCode = row["아이템 코드"] || row["itemCode"] || "";
- const itemName = row["아이템 명"] || row["itemName"] || "";
const workType = row["기능(공종)"] || row["workType"] || "";
- const description = row["설명"] || row["description"] || null;
- const itemList1 = row["항목1"] || row["itemList1"] || null;
- const itemList2 = row["항목2"] || row["itemList2"] || null;
- const itemList3 = row["항목3"] || row["itemList3"] || null;
- const itemList4 = row["항목4"] || row["itemList4"] || null;
+ const itemList = row["아이템 리스트"] || row["itemList"] || null;
+ const subItemList = row["서브 아이템 리스트"] || row["subItemList"] || null;
// 데이터 정제
const cleanedRow = {
itemCode: typeof itemCode === 'string' ? itemCode.trim() : String(itemCode).trim(),
- itemName: typeof itemName === 'string' ? itemName.trim() : String(itemName).trim(),
workType: typeof workType === 'string' ? workType.trim() : String(workType).trim(),
- description: description ? (typeof description === 'string' ? description : String(description)) : null,
- itemList1: itemList1 ? (typeof itemList1 === 'string' ? itemList1 : String(itemList1)) : null,
- itemList2: itemList2 ? (typeof itemList2 === 'string' ? itemList2 : String(itemList2)) : null,
- itemList3: itemList3 ? (typeof itemList3 === 'string' ? itemList3 : String(itemList3)) : null,
- itemList4: itemList4 ? (typeof itemList4 === 'string' ? itemList4 : String(itemList4)) : null,
+ itemList: itemList ? (typeof itemList === 'string' ? itemList : String(itemList)) : null,
+ subItemList: subItemList ? (typeof subItemList === 'string' ? subItemList : String(subItemList)) : null,
};
// 데이터 유효성 검사
@@ -101,13 +89,11 @@ export async function processHullFileImport(
// 해양 HULL 아이템 생성
const result = await createOffshoreHullItem({
itemCode: cleanedRow.itemCode,
- itemName: cleanedRow.itemName,
+ itemName: "기술영업", // 기본값 사용
workType: cleanedRow.workType as "HA" | "HE" | "HH" | "HM" | "NC",
- description: cleanedRow.description,
- itemList1: cleanedRow.itemList1,
- itemList2: cleanedRow.itemList2,
- itemList3: cleanedRow.itemList3,
- itemList4: cleanedRow.itemList4,
+ description: null,
+ itemList: cleanedRow.itemList,
+ subItemList: cleanedRow.subItemList,
});
if (result.success) {
diff --git a/lib/items-tech/table/hull/item-excel-template.tsx b/lib/items-tech/table/hull/item-excel-template.tsx
index f38bb9dc..61fddecc 100644
--- a/lib/items-tech/table/hull/item-excel-template.tsx
+++ b/lib/items-tech/table/hull/item-excel-template.tsx
@@ -19,13 +19,9 @@ export async function exportHullItemTemplate() {
// 컬럼 헤더 정의 및 스타일 적용
worksheet.columns = [
{ header: '아이템 코드', key: 'itemCode', width: 15 },
- { header: '아이템 명', key: 'itemName', width: 30 },
{ header: '기능(공종)', key: 'workType', width: 15 },
- { header: '설명', key: 'description', width: 50 },
- { header: '항목1', key: 'itemList1', width: 20 },
- { header: '항목2', key: 'itemList2', width: 20 },
- { header: '항목3', key: 'itemList3', width: 20 },
- { header: '항목4', key: 'itemList4', width: 20 },
+ { header: '아이템 리스트', key: 'itemList', width: 20 },
+ { header: '서브 아이템 리스트', key: 'subItemList', width: 20 },
];
// 헤더 스타일 적용
@@ -51,24 +47,16 @@ export async function exportHullItemTemplate() {
// 샘플 데이터 추가
const sampleData = [
{
- itemCode: 'HULL001',
- itemName: 'HULL 샘플 아이템 1',
+ itemCode: 'HULL001',
workType: 'HA',
- description: '이것은 해양 HULL 샘플 아이템 1의 설명입니다.',
- itemList1: '항목1 샘플 데이터',
- itemList2: '항목2 샘플 데이터',
- itemList3: '항목3 샘플 데이터',
- itemList4: '항목4 샘플 데이터'
+ itemList: '항목1 샘플 데이터',
+ subItemList: '항목2 샘플 데이터',
},
{
- itemCode: 'HULL002',
- itemName: 'HULL 샘플 아이템 2',
+ itemCode: 'HULL002',
workType: 'HE',
- description: '이것은 해양 HULL 샘플 아이템 2의 설명입니다.',
- itemList1: '항목1 샘플 데이터',
- itemList2: '항목2 샘플 데이터',
- itemList3: '',
- itemList4: ''
+ itemList: '항목1 샘플 데이터',
+ subItemList: '항목2 샘플 데이터',
}
];
@@ -94,7 +82,9 @@ export async function exportHullItemTemplate() {
// 워크시트에 공종 유형 관련 메모 추가
const infoRow = worksheet.addRow(['공종 유형 안내: ' + HULL_WORK_TYPES.join(', ')]);
infoRow.font = { bold: true, color: { argb: 'FF0000FF' } };
- worksheet.mergeCells(`A${infoRow.number}:H${infoRow.number}`);
+ worksheet.mergeCells(`A${infoRow.number}:F${infoRow.number}`);
+
+
// 워크시트 보호 (선택적)
worksheet.protect('', {
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 f5db40d8..7bc02173 100644
--- a/lib/items-tech/table/hull/offshore-hull-table-columns.tsx
+++ b/lib/items-tech/table/hull/offshore-hull-table-columns.tsx
@@ -23,10 +23,8 @@ interface OffshoreHullTableItem {
id: number;
itemId: number;
workType: "HA" | "HE" | "HH" | "HM" | "NC";
- itemList1: string | null;
- itemList2: string | null;
- itemList3: string | null;
- itemList4: string | null;
+ itemList: string | null;
+ subItemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -107,11 +105,10 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum
}
// ----------------------------------------------------------------
- // 3) 데이터 컬럼들을 그룹별로 구성
+ // 3) 데이터 컬럼들 정의
// ----------------------------------------------------------------
- // 3-1) 기본 정보 그룹 컬럼
- const basicInfoColumns: ColumnDef<OffshoreHullTableItem>[] = [
+ const dataColumns: ColumnDef<OffshoreHullTableItem>[] = [
{
accessorKey: "itemCode",
header: ({ column }) => (
@@ -122,20 +119,6 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "Material Group",
- group: "기본 정보",
- },
- },
- {
- accessorKey: "itemName",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Description" />
- ),
- cell: ({ row }) => <div>{row.original.itemName}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "Description",
- group: "기본 정보",
},
},
{
@@ -148,82 +131,32 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "기능(공종)",
- group: "기본 정보",
- },
- },
- {
- accessorKey: "description",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Size/Dimension" />
- ),
- cell: ({ row }) => <div>{row.original.description || "-"}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "Size/Dimension",
- group: "기본 정보",
- },
- },
- ]
-
- // 3-2) 아이템 리스트 그룹 컬럼
- const itemListColumns: ColumnDef<OffshoreHullTableItem>[] = [
- {
- accessorKey: "itemList1",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 1" />
- ),
- cell: ({ row }) => <div>{row.original.itemList1 || "-"}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "아이템 리스트 1",
- group: "아이템 리스트",
- },
- },
- {
- accessorKey: "itemList2",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 2" />
- ),
- cell: ({ row }) => <div>{row.original.itemList2 || "-"}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "아이템 리스트 2",
- group: "아이템 리스트",
},
},
{
- accessorKey: "itemList3",
+ accessorKey: "itemList",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 3" />
+ <DataTableColumnHeaderSimple column={column} title="아이템 리스트" />
),
- cell: ({ row }) => <div>{row.original.itemList3 || "-"}</div>,
+ cell: ({ row }) => <div>{row.original.itemList || "-"}</div>,
enableSorting: true,
enableHiding: true,
meta: {
- excelHeader: "아이템 리스트 3",
- group: "아이템 리스트",
+ excelHeader: "아이템 리스트",
},
},
{
- accessorKey: "itemList4",
+ accessorKey: "subItemList",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 4" />
+ <DataTableColumnHeaderSimple column={column} title="서브 아이템 리스트" />
),
- cell: ({ row }) => <div>{row.original.itemList4 || "-"}</div>,
+ cell: ({ row }) => <div>{row.original.subItemList || "-"}</div>,
enableSorting: true,
enableHiding: true,
meta: {
- excelHeader: "아이템 리스트 4",
- group: "아이템 리스트",
+ excelHeader: "서브 아이템 리스트",
},
},
- ]
-
- // 3-3) 메타데이터 그룹 컬럼
- const metadataColumns: ColumnDef<OffshoreHullTableItem>[] = [
{
accessorKey: "createdAt",
header: ({ column }) => (
@@ -234,7 +167,6 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "생성일",
- group: "Metadata",
},
},
{
@@ -247,36 +179,16 @@ export function getOffshoreHullColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "수정일",
- group: "Metadata",
},
}
]
-
- // 3-4) 그룹별 컬럼 구성
- const groupedColumns: ColumnDef<OffshoreHullTableItem>[] = [
- {
- id: "기본 정보",
- header: "기본 정보",
- columns: basicInfoColumns,
- },
- {
- id: "아이템 리스트",
- header: "아이템 리스트",
- columns: itemListColumns,
- },
- {
- id: "Metadata",
- header: "Metadata",
- columns: metadataColumns,
- }
- ]
// ----------------------------------------------------------------
- // 4) 최종 컬럼 배열: select, groupedColumns, actions
+ // 4) 최종 컬럼 배열: select, dataColumns, actions
// ----------------------------------------------------------------
return [
selectColumn,
- ...groupedColumns,
+ ...dataColumns,
actionsColumn,
]
} \ No newline at end of file
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 f5324cb1..ad6f86b2 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
@@ -24,10 +24,8 @@ interface OffshoreHullItem {
id: number;
itemId: number;
workType: "HA" | "HE" | "HH" | "HM" | "NC";
- itemList1: string | null;
- itemList2: string | null;
- itemList3: string | null;
- itemList4: string | null;
+ itemList: string | null;
+ subItemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -76,10 +74,8 @@ export function OffshoreHullTableToolbarActions({ table }: OffshoreHullTableTool
{ key: 'itemName', header: '아이템 명' },
{ key: 'description', header: '설명' },
{ key: 'workType', header: '기능(공종)' },
- { key: 'itemList1', header: '아이템 리스트 1' },
- { key: 'itemList2', header: '아이템 리스트 2' },
- { key: 'itemList3', header: '아이템 리스트 3' },
- { key: 'itemList4', header: '아이템 리스트 4' }
+ { key: 'itemList', header: '아이템 리스트' },
+ { key: 'subItemList', header: '서브 아이템 리스트' },
].filter(header => !excludeColumns.includes(header.key));
console.log("내보내기 헤더:", headers);
diff --git a/lib/items-tech/table/hull/offshore-hull-table.tsx b/lib/items-tech/table/hull/offshore-hull-table.tsx
index 8efa9c81..00f2c466 100644
--- a/lib/items-tech/table/hull/offshore-hull-table.tsx
+++ b/lib/items-tech/table/hull/offshore-hull-table.tsx
@@ -21,10 +21,8 @@ type OffshoreHullItem = {
id: number;
itemId: number;
workType: "HA" | "HE" | "HH" | "HM" | "NC";
- itemList1: string | null;
- itemList2: string | null;
- itemList3: string | null;
- itemList4: string | null;
+ itemList: string | null;
+ subItemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -56,8 +54,12 @@ export function OffshoreHullTable({ promises }: OffshoreHullTableProps) {
label: "기능(공종)",
},
{
- id: "itemList1",
- label: "아이템 리스트 1",
+ id: "itemList",
+ label: "아이템 리스트",
+ },
+ {
+ id: "subItemList",
+ label: "서브 아이템 리스트",
},
]
@@ -83,23 +85,13 @@ export function OffshoreHullTable({ promises }: OffshoreHullTableProps) {
type: "text",
},
{
- id: "itemList1",
- label: "아이템 리스트 1",
- type: "text",
- },
- {
- id: "itemList2",
- label: "아이템 리스트 2",
- type: "text",
- },
- {
- id: "itemList3",
- label: "아이템 리스트 3",
+ id: "itemList",
+ label: "아이템 리스트",
type: "text",
},
{
- id: "itemList4",
- label: "아이템 리스트 4",
+ id: "subItemList",
+ label: "서브 아이템 리스트",
type: "text",
},
]
diff --git a/lib/items-tech/table/ship/Items-ship-table.tsx b/lib/items-tech/table/ship/Items-ship-table.tsx
index aee90cfc..29ad8f8a 100644
--- a/lib/items-tech/table/ship/Items-ship-table.tsx
+++ b/lib/items-tech/table/ship/Items-ship-table.tsx
@@ -24,6 +24,7 @@ type ShipbuildingItem = {
shipTypes: string;
itemCode: string;
itemName: string;
+ itemList: string | null;
description: string | null;
createdAt: Date;
updatedAt: Date;
@@ -56,6 +57,10 @@ export function ItemsShipTable({ promises }: ItemsTableProps) {
id: "shipTypes",
label: "선종",
},
+ {
+ id: "itemList",
+ label: "아이템 리스트",
+ },
]
/**
@@ -95,6 +100,11 @@ export function ItemsShipTable({ promises }: ItemsTableProps) {
label: "선종",
type: "text",
},
+ {
+ id: "itemList",
+ label: "아이템 리스트",
+ type: "text",
+ },
]
const { table } = useDataTable({
diff --git a/lib/items-tech/table/ship/import-item-handler.tsx b/lib/items-tech/table/ship/import-item-handler.tsx
index 6ad24398..2a23d9d6 100644
--- a/lib/items-tech/table/ship/import-item-handler.tsx
+++ b/lib/items-tech/table/ship/import-item-handler.tsx
@@ -8,11 +8,10 @@ const SHIP_TYPES = ['A-MAX', 'S-MAX', 'LNGC', 'VLCC', 'CONT'] as const;
// 아이템 데이터 검증을 위한 Zod 스키마
const itemSchema = z.object({
itemCode: z.string().min(1, "아이템 코드는 필수입니다"),
- itemName: z.string().min(1, "아이템 명은 필수입니다"),
workType: z.enum(["기장", "전장", "선실", "배관", "철의"], {
required_error: "기능(공종)은 필수입니다",
}),
- description: z.string().nullable().optional(),
+ itemList: z.string().nullable().optional(),
});
interface ProcessResult {
@@ -25,7 +24,7 @@ interface ProcessResult {
* Excel 파일에서 가져온 조선 아이템 데이터 처리하는 함수
*/
export async function processFileImport(
- jsonData: any[],
+ jsonData: Record<string, unknown>[],
progressCallback?: (current: number, total: number) => void
): Promise<ProcessResult> {
// 결과 카운터 초기화
@@ -60,16 +59,14 @@ export async function processFileImport(
try {
// 필드 매핑 (한글/영문 필드명 모두 지원)
const itemCode = row["아이템 코드"] || row["itemCode"] || "";
- const itemName = row["아이템 명"] || row["itemName"] || "";
const workType = row["기능(공종)"] || row["workType"] || "";
- const description = row["설명"] || row["description"] || null;
+ const itemList = row["아이템 리스트"] || row["itemList"] || null;
// 데이터 정제
const cleanedRow = {
itemCode: typeof itemCode === 'string' ? itemCode.trim() : String(itemCode).trim(),
- itemName: typeof itemName === 'string' ? itemName.trim() : String(itemName).trim(),
workType: typeof workType === 'string' ? workType.trim() : String(workType).trim(),
- description: description ? (typeof description === 'string' ? description : String(description)) : null,
+ itemList: itemList ? (typeof itemList === 'string' ? itemList : String(itemList)) : null,
};
// 데이터 유효성 검사
@@ -88,27 +85,20 @@ export async function processFileImport(
// 선종 데이터 처리
const shipTypeEntries = SHIP_TYPES.map(type => ({
type,
- value: row[type]?.toUpperCase() === 'O'
+ value: row[type] ? String(row[type]).toUpperCase() === 'O' : false
})).filter(entry => entry.value);
console.log('shipTypeEntries:', shipTypeEntries);
+ // 선종이 없는 경우에 "option" 값을 사용
if (shipTypeEntries.length === 0) {
- errors.push({
- row: rowIndex,
- message: "최소 하나 이상의 선종이 'O'로 지정되어야 합니다."
- });
- errorCount++;
- continue;
- }
-
- // 각 선종에 대해 아이템 생성
- for (const { type } of shipTypeEntries) {
+ // "option" 값으로 아이템 생성
const result = await createShipbuildingImportItem({
itemCode: cleanedRow.itemCode,
- itemName: cleanedRow.itemName,
+ itemName: "기술영업", // 기본값 사용
workType: cleanedRow.workType as "기장" | "전장" | "선실" | "배관" | "철의",
- shipTypes: { [type]: true },
- description: cleanedRow.description,
+ shipTypes: { "OPTION": true },
+ description: null,
+ itemList: cleanedRow.itemList,
});
if (result.success || !result.error) {
@@ -116,10 +106,32 @@ export async function processFileImport(
} else {
errors.push({
row: rowIndex,
- message: `${type}: ${result.message || result.error || "알 수 없는 오류"}`
+ message: `OPTION: ${result.message || result.error || "알 수 없는 오류"}`
});
errorCount++;
}
+ } else {
+ // 각 선종에 대해 아이템 생성
+ for (const { type } of shipTypeEntries) {
+ const result = await createShipbuildingImportItem({
+ itemCode: cleanedRow.itemCode,
+ itemName: "기술영업", // 기본값 사용
+ workType: cleanedRow.workType as "기장" | "전장" | "선실" | "배관" | "철의",
+ shipTypes: { [type]: true },
+ description: null,
+ itemList: cleanedRow.itemList,
+ });
+
+ if (result.success || !result.error) {
+ successCount++;
+ } else {
+ errors.push({
+ row: rowIndex,
+ message: `${type}: ${result.message || result.error || "알 수 없는 오류"}`
+ });
+ errorCount++;
+ }
+ }
}
} catch (error) {
console.error(`${rowIndex}행 처리 오류:`, error);
diff --git a/lib/items-tech/table/ship/item-excel-template.tsx b/lib/items-tech/table/ship/item-excel-template.tsx
index 127a1dea..f6b20b6d 100644
--- a/lib/items-tech/table/ship/item-excel-template.tsx
+++ b/lib/items-tech/table/ship/item-excel-template.tsx
@@ -18,9 +18,8 @@ export async function exportItemTemplate() {
// 컬럼 헤더 정의 및 스타일 적용
worksheet.columns = [
{ header: '아이템 코드', key: 'itemCode', width: 15 },
- { header: '아이템 명', key: 'itemName', width: 30 },
{ header: '기능(공종)', key: 'workType', width: 15 },
- { header: '설명', key: 'description', width: 50 },
+ { header: '아이템 리스트', key: 'itemList', width: 30 },
...SHIP_TYPES.map(type => ({
header: type,
key: type,
@@ -52,25 +51,33 @@ export async function exportItemTemplate() {
const sampleData = [
{
itemCode: 'BG0001',
- itemName: '샘플 아이템 1',
workType: '기장',
- description: '이것은 샘플 아이템 1의 설명입니다.',
+ itemList: '아이템 리스트 내용',
'A-MAX': 'O',
'S-MAX': 'O',
'LNGC': 'O',
- 'VLCC': 'X',
- 'CONT': 'X'
+ 'VLCC': ' ',
+ 'CONT': ' '
},
{
itemCode: 'BG0002',
- itemName: '샘플 아이템 2',
workType: '전장',
- description: '이것은 샘플 아이템 2의 설명입니다.',
+ itemList: '아이템 리스트 내용 2',
'A-MAX': 'O',
- 'S-MAX': 'X',
+ 'S-MAX': ' ',
'LNGC': 'O',
'VLCC': 'O',
- 'CONT': 'X'
+ 'CONT': ' '
+ },
+ {
+ itemCode: 'BG0003',
+ workType: '선실',
+ itemList: '선종 없는 아이템',
+ 'A-MAX': ' ',
+ 'S-MAX': ' ',
+ 'LNGC': ' ',
+ 'VLCC': ' ',
+ 'CONT': ' '
}
];
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 2b46db92..29e1d503 100644
--- a/lib/items-tech/table/ship/items-ship-table-columns.tsx
+++ b/lib/items-tech/table/ship/items-ship-table-columns.tsx
@@ -24,6 +24,7 @@ interface ShipbuildingTableItem {
itemId: number;
workType: "기장" | "전장" | "선실" | "배관" | "철의";
shipTypes: string;
+ itemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -107,11 +108,10 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum
}
// ----------------------------------------------------------------
- // 3) 데이터 컬럼들을 그룹별로 구성
+ // 3) 데이터 컬럼들 정의
// ----------------------------------------------------------------
- // 3-1) 기본 정보 그룹 컬럼
- const basicInfoColumns: ColumnDef<ShipbuildingTableItem>[] = [
+ const dataColumns: ColumnDef<ShipbuildingTableItem>[] = [
{
accessorKey: "itemCode",
header: ({ column }) => (
@@ -122,20 +122,6 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "Material Group",
- group: "기본 정보",
- },
- },
- {
- accessorKey: "itemName",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Description" />
- ),
- cell: ({ row }) => <div>{row.original.itemName}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "Description",
- group: "기본 정보",
},
},
{
@@ -148,44 +134,32 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "기능(공종)",
- group: "기본 정보",
},
},
{
- accessorKey: "description",
+ accessorKey: "shipTypes",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Size/Dimension" />
+ <DataTableColumnHeaderSimple column={column} title="선종" />
),
- cell: ({ row }) => <div>{row.original.description || "-"}</div>,
+ cell: ({ row }) => <div>{row.original.shipTypes}</div>,
enableSorting: true,
enableHiding: true,
meta: {
- excelHeader: "Size/Dimension",
- group: "기본 정보",
+ excelHeader: "선종",
},
},
- ]
-
- // 3-2) 선종 정보 그룹 컬럼
- const shipTypesColumns: ColumnDef<ShipbuildingTableItem>[] = [
{
- accessorKey: "shipTypes",
+ accessorKey: "itemList",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="선종" />
+ <DataTableColumnHeaderSimple column={column} title="아이템 리스트" />
),
- cell: ({ row }) => <div>{row.original.shipTypes}</div>,
+ cell: ({ row }) => <div>{row.original.itemList || "-"}</div>,
enableSorting: true,
enableHiding: true,
meta: {
- excelHeader: "선종",
- group: "선종",
+ excelHeader: "아이템 리스트",
},
},
- ]
-
- // 3-3) 메타데이터 그룹 컬럼
- const metadataColumns: ColumnDef<ShipbuildingTableItem>[] = [
-
{
accessorKey: "createdAt",
header: ({ column }) => (
@@ -196,7 +170,6 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "생성일",
- group: "Metadata",
},
},
{
@@ -209,36 +182,16 @@ export function getShipbuildingColumns({ setRowAction }: GetColumnsProps): Colum
enableHiding: true,
meta: {
excelHeader: "수정일",
- group: "Metadata",
},
}
]
-
- // 3-4) 그룹별 컬럼 구성
- const groupedColumns: ColumnDef<ShipbuildingTableItem>[] = [
- {
- id: "기본 정보",
- header: "기본 정보",
- columns: basicInfoColumns,
- },
- {
- id: "선종",
- header: "선종",
- columns: shipTypesColumns,
- },
- {
- id: "Metadata",
- header: "Metadata",
- columns: metadataColumns,
- }
- ]
// ----------------------------------------------------------------
- // 4) 최종 컬럼 배열: select, groupedColumns, actions
+ // 4) 최종 컬럼 배열: select, dataColumns, actions
// ----------------------------------------------------------------
return [
selectColumn,
- ...groupedColumns,
+ ...dataColumns,
actionsColumn,
]
} \ No newline at end of file
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 6cae61af..677173d1 100644
--- a/lib/items-tech/table/ship/items-table-toolbar-actions.tsx
+++ b/lib/items-tech/table/ship/items-table-toolbar-actions.tsx
@@ -27,6 +27,7 @@ interface ShipbuildingItem {
shipTypes: string;
itemCode: string;
itemName: string;
+ itemList: string | null;
description: string | null;
createdAt: Date;
updatedAt: Date;
@@ -73,7 +74,8 @@ export function ItemsTableToolbarActions({ table }: ItemsTableToolbarActionsProp
{ key: 'itemName', header: '아이템 명' },
{ key: 'description', header: '설명' },
{ key: 'workType', header: '기능(공종)' },
- { key: 'shipTypes', header: '선종' }
+ { key: 'shipTypes', header: '선종' },
+ { key: 'itemList', 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 de1638a8..d6b81964 100644
--- a/lib/items-tech/table/top/import-item-handler.tsx
+++ b/lib/items-tech/table/top/import-item-handler.tsx
@@ -9,15 +9,11 @@ const TOP_WORK_TYPES = ["TM", "TS", "TE", "TP"] as const;
// 아이템 데이터 검증을 위한 Zod 스키마
const itemSchema = z.object({
itemCode: z.string().min(1, "아이템 코드는 필수입니다"),
- itemName: z.string().min(1, "아이템 명은 필수입니다"),
workType: z.enum(TOP_WORK_TYPES, {
required_error: "기능(공종)은 필수입니다",
}),
- description: z.string().nullable().optional(),
- itemList1: z.string().nullable().optional(),
- itemList2: z.string().nullable().optional(),
- itemList3: z.string().nullable().optional(),
- itemList4: z.string().nullable().optional(),
+ itemList: z.string().nullable().optional(),
+ subItemList: z.string().nullable().optional(),
});
interface ProcessResult {
@@ -30,7 +26,7 @@ interface ProcessResult {
* Excel 파일에서 가져온 해양 TOP 아이템 데이터 처리하는 함수
*/
export async function processTopFileImport(
- jsonData: any[],
+ jsonData: Record<string, unknown>[],
progressCallback?: (current: number, total: number) => void
): Promise<ProcessResult> {
// 결과 카운터 초기화
@@ -65,24 +61,16 @@ export async function processTopFileImport(
try {
// 필드 매핑 (한글/영문 필드명 모두 지원)
const itemCode = row["아이템 코드"] || row["itemCode"] || "";
- const itemName = row["아이템 명"] || row["itemName"] || "";
const workType = row["기능(공종)"] || row["workType"] || "";
- const description = row["설명"] || row["description"] || null;
- const itemList1 = row["항목1"] || row["itemList1"] || null;
- const itemList2 = row["항목2"] || row["itemList2"] || null;
- const itemList3 = row["항목3"] || row["itemList3"] || null;
- const itemList4 = row["항목4"] || row["itemList4"] || null;
+ const itemList = row["아이템 리스트"] || row["itemList"] || null;
+ const subItemList = row["서브 아이템 리스트"] || row["subItemList"] || null;
// 데이터 정제
const cleanedRow = {
itemCode: typeof itemCode === 'string' ? itemCode.trim() : String(itemCode).trim(),
- itemName: typeof itemName === 'string' ? itemName.trim() : String(itemName).trim(),
workType: typeof workType === 'string' ? workType.trim() : String(workType).trim(),
- description: description ? (typeof description === 'string' ? description : String(description)) : null,
- itemList1: itemList1 ? (typeof itemList1 === 'string' ? itemList1 : String(itemList1)) : null,
- itemList2: itemList2 ? (typeof itemList2 === 'string' ? itemList2 : String(itemList2)) : null,
- itemList3: itemList3 ? (typeof itemList3 === 'string' ? itemList3 : String(itemList3)) : null,
- itemList4: itemList4 ? (typeof itemList4 === 'string' ? itemList4 : String(itemList4)) : null,
+ itemList: itemList ? (typeof itemList === 'string' ? itemList : String(itemList)) : null,
+ subItemList: subItemList ? (typeof subItemList === 'string' ? subItemList : String(subItemList)) : null,
};
// 데이터 유효성 검사
@@ -101,13 +89,11 @@ export async function processTopFileImport(
// 해양 TOP 아이템 생성
const result = await createOffshoreTopItem({
itemCode: cleanedRow.itemCode,
- itemName: cleanedRow.itemName,
+ itemName: "기술영업", // 기본값 사용
workType: cleanedRow.workType as "TM" | "TS" | "TE" | "TP",
- description: cleanedRow.description,
- itemList1: cleanedRow.itemList1,
- itemList2: cleanedRow.itemList2,
- itemList3: cleanedRow.itemList3,
- itemList4: cleanedRow.itemList4,
+ description: null,
+ itemList: cleanedRow.itemList,
+ subItemList: cleanedRow.subItemList,
});
if (result.success) {
diff --git a/lib/items-tech/table/top/item-excel-template.tsx b/lib/items-tech/table/top/item-excel-template.tsx
index 4514af59..f0e10d82 100644
--- a/lib/items-tech/table/top/item-excel-template.tsx
+++ b/lib/items-tech/table/top/item-excel-template.tsx
@@ -19,13 +19,10 @@ export async function exportTopItemTemplate() {
// 컬럼 헤더 정의 및 스타일 적용
worksheet.columns = [
{ header: '아이템 코드', key: 'itemCode', width: 15 },
- { header: '아이템 명', key: 'itemName', width: 30 },
{ header: '기능(공종)', key: 'workType', width: 15 },
- { header: '설명', key: 'description', width: 50 },
- { header: '항목1', key: 'itemList1', width: 20 },
- { header: '항목2', key: 'itemList2', width: 20 },
- { header: '항목3', key: 'itemList3', width: 20 },
- { header: '항목4', key: 'itemList4', width: 20 },
+ { header: '아이템 리스트', key: 'itemList', width: 20 },
+ { header: '서브 아이템 리스트', key: 'subItemList', width: 20 },
+
];
// 헤더 스타일 적용
@@ -51,25 +48,18 @@ export async function exportTopItemTemplate() {
// 샘플 데이터 추가
const sampleData = [
{
- itemCode: 'TOP001',
- itemName: 'TOP 샘플 아이템 1',
+ itemCode: 'TOP001',
workType: 'TM',
- description: '이것은 해양 TOP 샘플 아이템 1의 설명입니다.',
- itemList1: '항목1 샘플 데이터',
- itemList2: '항목2 샘플 데이터',
- itemList3: '항목3 샘플 데이터',
- itemList4: '항목4 샘플 데이터'
+ itemList: '항목1 샘플 데이터',
+ subItemList: '항목2 샘플 데이터',
},
{
- itemCode: 'TOP002',
- itemName: 'TOP 샘플 아이템 2',
+ itemCode: 'TOP002',
workType: 'TS',
- description: '이것은 해양 TOP 샘플 아이템 2의 설명입니다.',
- itemList1: '항목1 샘플 데이터',
- itemList2: '항목2 샘플 데이터',
- itemList3: '',
- itemList4: ''
+ itemList: '항목1 샘플 데이터',
+ subItemList: '항목2 샘플 데이터',
}
+
];
// 데이터 행 추가
@@ -94,7 +84,8 @@ export async function exportTopItemTemplate() {
// 워크시트에 공종 유형 관련 메모 추가
const infoRow = worksheet.addRow(['공종 유형 안내: ' + TOP_WORK_TYPES.join(', ')]);
infoRow.font = { bold: true, color: { argb: 'FF0000FF' } };
- worksheet.mergeCells(`A${infoRow.number}:H${infoRow.number}`);
+ 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 4ccb2003..4746f226 100644
--- a/lib/items-tech/table/top/offshore-top-table-columns.tsx
+++ b/lib/items-tech/table/top/offshore-top-table-columns.tsx
@@ -23,10 +23,8 @@ interface OffshoreTopTableItem {
id: number;
itemId: number;
workType: "TM" | "TS" | "TE" | "TP";
- itemList1: string | null;
- itemList2: string | null;
- itemList3: string | null;
- itemList4: string | null;
+ itemList: string | null;
+ subItemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -107,11 +105,10 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column
}
// ----------------------------------------------------------------
- // 3) 데이터 컬럼들을 그룹별로 구성
+ // 3) 데이터 컬럼들 정의
// ----------------------------------------------------------------
- // 3-1) 기본 정보 그룹 컬럼
- const basicInfoColumns: ColumnDef<OffshoreTopTableItem>[] = [
+ const dataColumns: ColumnDef<OffshoreTopTableItem>[] = [
{
accessorKey: "itemCode",
header: ({ column }) => (
@@ -122,20 +119,6 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column
enableHiding: true,
meta: {
excelHeader: "Material Group",
- group: "기본 정보",
- },
- },
- {
- accessorKey: "itemName",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Description" />
- ),
- cell: ({ row }) => <div>{row.original.itemName}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "Description",
- group: "기본 정보",
},
},
{
@@ -148,82 +131,32 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column
enableHiding: true,
meta: {
excelHeader: "기능(공종)",
- group: "기본 정보",
- },
- },
- {
- accessorKey: "description",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="Size/Dimension" />
- ),
- cell: ({ row }) => <div>{row.original.description || "-"}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "Size/Dimension",
- group: "기본 정보",
- },
- },
- ]
-
- // 3-2) 아이템 리스트 그룹 컬럼
- const itemListColumns: ColumnDef<OffshoreTopTableItem>[] = [
- {
- accessorKey: "itemList1",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 1" />
- ),
- cell: ({ row }) => <div>{row.original.itemList1 || "-"}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "아이템 리스트 1",
- group: "아이템 리스트",
- },
- },
- {
- accessorKey: "itemList2",
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 2" />
- ),
- cell: ({ row }) => <div>{row.original.itemList2 || "-"}</div>,
- enableSorting: true,
- enableHiding: true,
- meta: {
- excelHeader: "아이템 리스트 2",
- group: "아이템 리스트",
},
},
{
- accessorKey: "itemList3",
+ accessorKey: "itemList",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 3" />
+ <DataTableColumnHeaderSimple column={column} title="아이템 리스트" />
),
- cell: ({ row }) => <div>{row.original.itemList3 || "-"}</div>,
+ cell: ({ row }) => <div>{row.original.itemList || "-"}</div>,
enableSorting: true,
enableHiding: true,
meta: {
- excelHeader: "아이템 리스트 3",
- group: "아이템 리스트",
+ excelHeader: "아이템 리스트",
},
},
{
- accessorKey: "itemList4",
+ accessorKey: "subItemList",
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="아이템 리스트 4" />
+ <DataTableColumnHeaderSimple column={column} title="서브 아이템 리스트" />
),
- cell: ({ row }) => <div>{row.original.itemList4 || "-"}</div>,
+ cell: ({ row }) => <div>{row.original.subItemList || "-"}</div>,
enableSorting: true,
enableHiding: true,
meta: {
- excelHeader: "아이템 리스트 4",
- group: "아이템 리스트",
+ excelHeader: "서브 아이템 리스트",
},
},
- ]
-
- // 3-3) 메타데이터 그룹 컬럼
- const metadataColumns: ColumnDef<OffshoreTopTableItem>[] = [
{
accessorKey: "createdAt",
header: ({ column }) => (
@@ -234,7 +167,6 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column
enableHiding: true,
meta: {
excelHeader: "생성일",
- group: "Metadata",
},
},
{
@@ -247,36 +179,16 @@ export function getOffshoreTopColumns({ setRowAction }: GetColumnsProps): Column
enableHiding: true,
meta: {
excelHeader: "수정일",
- group: "Metadata",
},
}
]
-
- // 3-4) 그룹별 컬럼 구성
- const groupedColumns: ColumnDef<OffshoreTopTableItem>[] = [
- {
- id: "기본 정보",
- header: "기본 정보",
- columns: basicInfoColumns,
- },
- {
- id: "아이템 리스트",
- header: "아이템 리스트",
- columns: itemListColumns,
- },
- {
- id: "Metadata",
- header: "Metadata",
- columns: metadataColumns,
- }
- ]
// ----------------------------------------------------------------
- // 4) 최종 컬럼 배열: select, groupedColumns, actions
+ // 4) 최종 컬럼 배열: select, dataColumns, actions
// ----------------------------------------------------------------
return [
selectColumn,
- ...groupedColumns,
+ ...dataColumns,
actionsColumn,
]
} \ No newline at end of file
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 324312aa..f91adf96 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
@@ -24,10 +24,8 @@ interface OffshoreTopItem {
id: number;
itemId: number;
workType: "TM" | "TS" | "TE" | "TP";
- itemList1: string | null;
- itemList2: string | null;
- itemList3: string | null;
- itemList4: string | null;
+ itemList: string | null;
+ subItemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -76,10 +74,8 @@ export function OffshoreTopTableToolbarActions({ table }: OffshoreTopTableToolba
{ key: 'itemName', header: '아이템 명' },
{ key: 'description', header: '설명' },
{ key: 'workType', header: '기능(공종)' },
- { key: 'itemList1', header: '아이템 리스트 1' },
- { key: 'itemList2', header: '아이템 리스트 2' },
- { key: 'itemList3', header: '아이템 리스트 3' },
- { key: 'itemList4', header: '아이템 리스트 4' }
+ { key: 'itemList', header: '아이템 리스트' },
+ { key: 'subItemList', header: '서브 아이템 리스트' },
].filter(header => !excludeColumns.includes(header.key));
console.log("내보내기 헤더:", headers);
diff --git a/lib/items-tech/table/top/offshore-top-table.tsx b/lib/items-tech/table/top/offshore-top-table.tsx
index dedf766a..689f1db3 100644
--- a/lib/items-tech/table/top/offshore-top-table.tsx
+++ b/lib/items-tech/table/top/offshore-top-table.tsx
@@ -21,10 +21,8 @@ type OffshoreTopItem = {
id: number;
itemId: number;
workType: "TM" | "TS" | "TE" | "TP";
- itemList1: string | null;
- itemList2: string | null;
- itemList3: string | null;
- itemList4: string | null;
+ itemList: string | null;
+ subItemList: string | null;
itemCode: string;
itemName: string;
description: string | null;
@@ -56,8 +54,8 @@ export function OffshoreTopTable({ promises }: OffshoreTopTableProps) {
label: "기능(공종)",
},
{
- id: "itemList1",
- label: "아이템 리스트 1",
+ id: "itemList",
+ label: "아이템 리스트",
},
]
@@ -83,25 +81,16 @@ export function OffshoreTopTable({ promises }: OffshoreTopTableProps) {
type: "text",
},
{
- id: "itemList1",
- label: "아이템 리스트 1",
+ id: "itemList",
+ label: "아이템 리스트",
type: "text",
},
{
- id: "itemList2",
- label: "아이템 리스트 2",
- type: "text",
- },
- {
- id: "itemList3",
- label: "아이템 리스트 3",
- type: "text",
- },
- {
- id: "itemList4",
- label: "아이템 리스트 4",
+ id: "subItemList",
+ label: "서브 아이템 리스트",
type: "text",
},
+
]
const { table } = useDataTable({
diff --git a/lib/items-tech/table/update-items-sheet.tsx b/lib/items-tech/table/update-items-sheet.tsx
index 2db3b193..efab4b1a 100644
--- a/lib/items-tech/table/update-items-sheet.tsx
+++ b/lib/items-tech/table/update-items-sheet.tsx
@@ -29,7 +29,6 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
-import { Textarea } from "@/components/ui/textarea"
import { toast } from "sonner"
import {
@@ -75,22 +74,19 @@ interface CommonItemFields {
type ShipbuildingItem = CommonItemFields & {
workType: "기장" | "전장" | "선실" | "배관" | "철의"
shipTypes: string
+ itemList: string | null
}
type OffshoreTopItem = CommonItemFields & {
workType: "TM" | "TS" | "TE" | "TP"
- itemList1: string | null
- itemList2: string | null
- itemList3: string | null
- itemList4: string | null
+ itemList: string | null
+ subItemList: string | null
}
type OffshoreHullItem = CommonItemFields & {
workType: "HA" | "HE" | "HH" | "HM" | "NC"
- itemList1: string | null
- itemList2: string | null
- itemList3: string | null
- itemList4: string | null
+ itemList: string | null
+ subItemList: string | null
}
type UpdateItemSchema = {
@@ -99,10 +95,8 @@ type UpdateItemSchema = {
description?: string
workType?: string
shipTypes?: string
- itemList1?: string
- itemList2?: string
- itemList3?: string
- itemList4?: string
+ itemList?: string
+ subItemList?: string
}
interface UpdateItemSheetProps {
@@ -118,7 +112,6 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
// 초기값 설정
const form = useForm<UpdateItemSchema>({
defaultValues: {
- itemCode: item.itemCode,
itemName: item.itemName,
description: item.description || "",
workType: item.workType,
@@ -133,16 +126,15 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
switch (itemType) {
case 'shipbuilding':
return {
- shipTypes: (item as ShipbuildingItem).shipTypes
+ shipTypes: (item as ShipbuildingItem).shipTypes,
+ itemList: (item as ShipbuildingItem).itemList || ""
};
case 'offshoreTop':
case 'offshoreHull':
const offshoreItem = item as OffshoreTopItem | OffshoreHullItem;
return {
- itemList1: offshoreItem.itemList1 || "",
- itemList2: offshoreItem.itemList2 || "",
- itemList3: offshoreItem.itemList3 || "",
- itemList4: offshoreItem.itemList4 || "",
+ itemList: offshoreItem.itemList || "",
+ subItemList: offshoreItem.subItemList || ""
};
default:
return {};
@@ -158,19 +150,22 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
case 'shipbuilding':
result = await modifyShipbuildingItem({
...data,
- id: item.id
+ id: item.id,
+ itemCode: item.itemCode // itemCode는 변경 불가, 원래 값 사용
});
break;
case 'offshoreTop':
result = await modifyOffshoreTopItem({
...data,
- id: item.id
+ id: item.id,
+ itemCode: item.itemCode // itemCode는 변경 불가, 원래 값 사용
});
break;
case 'offshoreHull':
result = await modifyOffshoreHullItem({
...data,
- id: item.id
+ id: item.id,
+ itemCode: item.itemCode // itemCode는 변경 불가, 원래 값 사용
});
break;
default:
@@ -227,34 +222,19 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
{getItemTypeLabel()} 정보를 수정합니다. 수정할 필드를 입력해주세요.
</SheetDescription>
</SheetHeader>
+
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
- <FormField
- control={form.control}
- name="itemCode"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Material Group</FormLabel>
- <FormControl>
- <Input placeholder="Material Group을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name="itemName"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Description</FormLabel>
- <FormControl>
- <Input placeholder="Description을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
+ {/* Material Group을 Form 안으로 이동 */}
+ <div className="mt-4">
+ <div className="grid gap-2">
+ <label className="text-sm font-medium leading-none">
+ Material Group (수정 불가)
+ </label>
+ <Input value={item.itemCode} disabled readOnly />
+ </div>
+ </div>
+
<FormField
control={form.control}
name="workType"
@@ -285,32 +265,15 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
{/* 조선 아이템 전용 필드 */}
{itemType === 'shipbuilding' && (
- <FormField
- control={form.control}
- name="shipTypes"
- render={({ field }) => (
- <FormItem>
- <FormLabel>선종</FormLabel>
- <FormControl>
- <Input placeholder="선종을 입력하세요" {...field} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- )}
-
- {/* 해양 TOP 또는 HULL 아이템 전용 필드 */}
- {(itemType === 'offshoreTop' || itemType === 'offshoreHull') && (
<>
<FormField
control={form.control}
- name="itemList1"
+ name="shipTypes"
render={({ field }) => (
<FormItem>
- <FormLabel>Item List 1</FormLabel>
+ <FormLabel>선종</FormLabel>
<FormControl>
- <Input placeholder="Item List 1을 입력하세요" {...field} />
+ <Input placeholder="선종을 입력하세요" {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -318,25 +281,31 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
/>
<FormField
control={form.control}
- name="itemList2"
+ name="itemList"
render={({ field }) => (
<FormItem>
- <FormLabel>Item List 2</FormLabel>
+ <FormLabel>아이템 리스트</FormLabel>
<FormControl>
- <Input placeholder="Item List 2를 입력하세요" {...field} />
+ <Input placeholder="아이템 리스트를 입력하세요" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
+ </>
+ )}
+
+ {/* 해양 TOP 또는 HULL 아이템 전용 필드 */}
+ {(itemType === 'offshoreTop' || itemType === 'offshoreHull') && (
+ <>
<FormField
control={form.control}
- name="itemList3"
+ name="itemList"
render={({ field }) => (
<FormItem>
- <FormLabel>Item List 3</FormLabel>
+ <FormLabel>아이템 리스트</FormLabel>
<FormControl>
- <Input placeholder="Item List 3을 입력하세요" {...field} />
+ <Input placeholder="아이템 리스트를 입력하세요" {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -344,12 +313,12 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
/>
<FormField
control={form.control}
- name="itemList4"
+ name="subItemList"
render={({ field }) => (
<FormItem>
- <FormLabel>Item List 4</FormLabel>
+ <FormLabel>서브 아이템 리스트</FormLabel>
<FormControl>
- <Input placeholder="Item List 4를 입력하세요" {...field} />
+ <Input placeholder="서브 아이템 리스트를 입력하세요" {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -357,23 +326,6 @@ export function UpdateItemSheet({ item, itemType, open, onOpenChange }: UpdateIt
/>
</>
)}
-
- <FormField
- control={form.control}
- name="description"
- render={({ field }) => (
- <FormItem>
- <FormLabel>Size/Dimension</FormLabel>
- <FormControl>
- <Textarea
- placeholder="Size/Dimension을 입력하세요"
- {...field}
- />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
<SheetFooter>
<SheetClose asChild>
<Button variant="outline">취소</Button>
diff --git a/lib/items-tech/validations.ts b/lib/items-tech/validations.ts
index 7c8a58f9..399193b2 100644
--- a/lib/items-tech/validations.ts
+++ b/lib/items-tech/validations.ts
@@ -48,6 +48,7 @@ export const updateShipbuildingItemSchema = z.object({
description: z.string().optional(),
workType: z.string().optional(),
shipTypes: z.string().optional(),
+ itemList: z.string().optional(),
})
export type GetItemsSchema = Awaited<ReturnType<typeof searchParamsCache.parse>>
@@ -62,6 +63,7 @@ export const createShipbuildingItemSchema = z.object({
description: z.string(),
workType: z.string(),
shipTypes: z.string(),
+ itemList: z.string().optional(),
})
export type CreateShipbuildingItemSchema = z.infer<typeof createShipbuildingItemSchema>
@@ -77,6 +79,7 @@ export interface ItemCreateData {
export interface ShipbuildingItemCreateData extends ItemCreateData {
workType: string | null
shipTypes: string | null
+ itemList?: string | null
}
// 아이템 타입에 따른 생성 데이터 타입
@@ -88,10 +91,8 @@ export const createOffshoreTopItemSchema = z.object({
itemName: z.string(),
description: z.string(),
workType: z.enum(["TM", "TS", "TE", "TP"]),
- itemList1: z.string().optional(),
- itemList2: z.string().optional(),
- itemList3: z.string().optional(),
- itemList4: z.string().optional(),
+ itemList: z.string().optional(),
+ subItemList: z.string().optional(),
})
// 해양 HULL 아이템 스키마
@@ -100,10 +101,8 @@ export const createOffshoreHullItemSchema = z.object({
itemName: z.string(),
description: z.string(),
workType: z.enum(["HA", "HE", "HH", "HM", "NC"]),
- itemList1: z.string().optional(),
- itemList2: z.string().optional(),
- itemList3: z.string().optional(),
- itemList4: z.string().optional(),
+ itemList: z.string().optional(),
+ subItemList: z.string().optional(),
})
export type CreateOffshoreTopItemSchema = z.infer<typeof createOffshoreTopItemSchema>
@@ -115,10 +114,8 @@ export const updateOffshoreTopItemSchema = z.object({
itemName: z.string().optional(),
description: z.string().optional(),
workType: z.enum(["TM", "TS", "TE", "TP"]).optional(),
- itemList1: z.string().optional(),
- itemList2: z.string().optional(),
- itemList3: z.string().optional(),
- itemList4: z.string().optional(),
+ itemList: z.string().optional(),
+ subItemList: z.string().optional(),
})
// 해양 HULL 아이템 업데이트 스키마
@@ -127,10 +124,8 @@ export const updateOffshoreHullItemSchema = z.object({
itemName: z.string().optional(),
description: z.string().optional(),
workType: z.enum(["HA", "HE", "HH", "HM", "NC"]).optional(),
- itemList1: z.string().optional(),
- itemList2: z.string().optional(),
- itemList3: z.string().optional(),
- itemList4: z.string().optional(),
+ itemList: z.string().optional(),
+ subItemList: z.string().optional(),
})
export type UpdateOffshoreTopItemSchema = z.infer<typeof updateOffshoreTopItemSchema>
@@ -139,18 +134,14 @@ export type UpdateOffshoreHullItemSchema = z.infer<typeof updateOffshoreHullItem
// 해양 TOP 아이템 생성 데이터 타입
export interface OffshoreTopItemCreateData extends ItemCreateData {
workType: "TM" | "TS" | "TE" | "TP"
- itemList1?: string | null
- itemList2?: string | null
- itemList3?: string | null
- itemList4?: string | null
+ itemList?: string | null
+ subItemList?: string | null
}
// 해양 HULL 아이템 생성 데이터 타입
export interface OffshoreHullItemCreateData extends ItemCreateData {
workType: "HA" | "HE" | "HH" | "HM" | "NC"
- itemList1?: string | null
- itemList2?: string | null
- itemList3?: string | null
- itemList4?: string | null
+ itemList?: string | null
+ subItemList?: string | null
}