summaryrefslogtreecommitdiff
path: root/lib/items
diff options
context:
space:
mode:
Diffstat (limited to 'lib/items')
-rw-r--r--lib/items/service.ts55
-rw-r--r--lib/items/table/add-items-dialog.tsx203
-rw-r--r--lib/items/table/import-excel-button.tsx28
-rw-r--r--lib/items/table/import-item-handler.tsx44
-rw-r--r--lib/items/table/item-excel-template.tsx42
-rw-r--r--lib/items/table/items-table.tsx49
-rw-r--r--lib/items/table/update-item-sheet.tsx199
-rw-r--r--lib/items/validations.ts30
8 files changed, 594 insertions, 56 deletions
diff --git a/lib/items/service.ts b/lib/items/service.ts
index 226742ca..99ef79ef 100644
--- a/lib/items/service.ts
+++ b/lib/items/service.ts
@@ -45,9 +45,18 @@ export async function getItems(input: GetItemsSchema) {
let globalWhere
if (input.search) {
const s = `%${input.search}%`
- globalWhere = or(ilike(items.itemCode, s), ilike(items.itemName, s)
- , ilike(items.description, s)
- )
+ globalWhere = or(
+ ilike(items.itemLevel, s),
+ ilike(items.itemCode, s),
+ ilike(items.itemName, s),
+ ilike(items.description, s),
+ ilike(items.parentItemCode, s),
+ ilike(items.unitOfMeasure, s),
+ ilike(items.steelType, s),
+ ilike(items.gradeMaterial, s),
+ ilike(items.baseUnitOfMeasure, s),
+ ilike(items.changeDate, s)
+ )
// 필요시 여러 칼럼 OR조건 (status, priority, etc)
}
@@ -87,6 +96,7 @@ export async function getItems(input: GetItemsSchema) {
return { data, pageCount };
} catch (err) {
// 에러 발생 시 디폴트
+ console.error(err)
return { data: [], pageCount: 0 };
}
},
@@ -106,6 +116,14 @@ export interface ItemCreateData {
itemCode: string
itemName: string
description: string | null
+ parentItemCode?: string | null
+ itemLevel?: number | null
+ deleteFlag?: string | null
+ unitOfMeasure?: string | null
+ steelType?: string | null
+ gradeMaterial?: string | null
+ changeDate?: string | null
+ baseUnitOfMeasure?: string | null
}
@@ -142,6 +160,14 @@ export async function createItem(input: ItemCreateData) {
txResult = await updateItem(tx, existingItem.id, {
itemName: input.itemName,
description: input.description,
+ parentItemCode: input.parentItemCode,
+ itemLevel: input.itemLevel,
+ deleteFlag: input.deleteFlag,
+ unitOfMeasure: input.unitOfMeasure,
+ steelType: input.steelType,
+ gradeMaterial: input.gradeMaterial,
+ changeDate: input.changeDate,
+ baseUnitOfMeasure: input.baseUnitOfMeasure,
})
} else {
// 새 아이템 생성
@@ -149,6 +175,14 @@ export async function createItem(input: ItemCreateData) {
itemCode: input.itemCode,
itemName: input.itemName,
description: input.description,
+ parentItemCode: input.parentItemCode,
+ itemLevel: input.itemLevel,
+ deleteFlag: input.deleteFlag,
+ unitOfMeasure: input.unitOfMeasure,
+ steelType: input.steelType,
+ gradeMaterial: input.gradeMaterial,
+ changeDate: input.changeDate,
+ baseUnitOfMeasure: input.baseUnitOfMeasure,
})
}
@@ -193,13 +227,20 @@ export async function createItem(input: ItemCreateData) {
export async function modifyItem(input: UpdateItemSchema & { id: number }) {
unstable_noStore();
try {
- const data = await db.transaction(async (tx) => {
- const [res] = await updateItem(tx, input.id, {
+ await db.transaction(async (tx) => {
+ await updateItem(tx, input.id, {
itemCode: input.itemCode,
itemName: input.itemName,
description: input.description,
+ parentItemCode: input.parentItemCode,
+ itemLevel: input.itemLevel,
+ deleteFlag: input.deleteFlag,
+ unitOfMeasure: input.unitOfMeasure,
+ steelType: input.steelType,
+ gradeMaterial: input.gradeMaterial,
+ changeDate: input.changeDate,
+ baseUnitOfMeasure: input.baseUnitOfMeasure,
});
- return res;
});
revalidateTag("items");
@@ -250,6 +291,6 @@ export async function getAllItems(): Promise<Item[]> {
try {
return await findAllItems();
} catch (err) {
- throw new Error("Failed to get roles");
+ throw new Error("Failed to get items");
}
}
diff --git a/lib/items/table/add-items-dialog.tsx b/lib/items/table/add-items-dialog.tsx
index 2224444c..3f71b437 100644
--- a/lib/items/table/add-items-dialog.tsx
+++ b/lib/items/table/add-items-dialog.tsx
@@ -20,7 +20,6 @@ import {
import {
Select,
SelectContent,
- SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
@@ -41,11 +40,31 @@ export function AddItemDialog() {
itemCode: "",
itemName: "",
description: "",
+ parentItemCode: "",
+ itemLevel: 5, // 기본값 5
+ deleteFlag: "N", // 기본값 N
+ unitOfMeasure: "",
+ steelType: "",
+ gradeMaterial: "",
+ changeDate: "",
+ baseUnitOfMeasure: "",
},
})
async function onSubmit(data: CreateItemSchema) {
- const result = await createItem(data)
+ const result = await createItem({
+ itemCode: data.itemCode,
+ itemName: data.itemName,
+ description: data.description ?? null,
+ parentItemCode: data.parentItemCode ?? null,
+ itemLevel: data.itemLevel ?? null,
+ deleteFlag: data.deleteFlag ?? null,
+ unitOfMeasure: data.unitOfMeasure ?? null,
+ steelType: data.steelType ?? null,
+ gradeMaterial: data.gradeMaterial ?? null,
+ changeDate: data.changeDate ?? null,
+ baseUnitOfMeasure: data.baseUnitOfMeasure ?? null,
+ })
if (result.error) {
alert(`에러: ${result.error}`)
return
@@ -83,16 +102,16 @@ export function AddItemDialog() {
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="space-y-4 py-4">
-
+ <p className="text-red-500 font-bold text-center">※ MDG에서 수신만 하기에 비활성화할 기능입니다. <br />직접 업로드는 기술영업만 가능</p>
<FormField
control={form.control}
name="itemCode"
render={({ field }) => (
<FormItem>
- <FormLabel>Item Code</FormLabel>
+ <FormLabel>자재그룹코드 *</FormLabel>
<FormControl>
<Input
- placeholder="e.g."
+ placeholder="예: BOLT001"
{...field}
/>
</FormControl>
@@ -105,10 +124,10 @@ export function AddItemDialog() {
name="itemName"
render={({ field }) => (
<FormItem>
- <FormLabel>Item Name</FormLabel>
+ <FormLabel>자재그룹명 *</FormLabel>
<FormControl>
<Input
- placeholder="e.g."
+ placeholder="예: 육각볼트 M8x20"
{...field}
/>
</FormControl>
@@ -122,11 +141,12 @@ export function AddItemDialog() {
name="description"
render={({ field }) => (
<FormItem>
- <FormLabel>Description</FormLabel>
+ <FormLabel>상세</FormLabel>
<FormControl>
<Textarea
- placeholder="e.g."
+ placeholder="예: 표준 육각 볼트 스테인리스 스틸"
{...field}
+ value={field.value || ""}
/>
</FormControl>
<FormMessage />
@@ -134,6 +154,171 @@ export function AddItemDialog() {
)}
/>
+ {/* 2열 레이아웃 */}
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="itemLevel"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>레벨</FormLabel>
+ <FormControl>
+ <Input
+ type="number"
+ min="1"
+ max="5"
+ placeholder="1-5"
+ {...field}
+ value={field.value || ""}
+ onChange={(e) => field.onChange(e.target.value ? parseInt(e.target.value) : null)}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="deleteFlag"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>삭제 플래그</FormLabel>
+ <FormControl>
+ <Select onValueChange={field.onChange} defaultValue={field.value || "N"}>
+ <SelectTrigger>
+ <SelectValue placeholder="선택" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="N">N (사용)</SelectItem>
+ <SelectItem value="Y">Y (삭제)</SelectItem>
+ </SelectContent>
+ </Select>
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="parentItemCode"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>부모 아이템 코드</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: BOLT001"
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="steelType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>강종</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: SS, CS, AL"
+ maxLength={2}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="unitOfMeasure"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>단위</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: EA, SET, KG"
+ maxLength={3}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="baseUnitOfMeasure"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>기본단위</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: EA, SET, KG"
+ maxLength={3}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="gradeMaterial"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>등급 재질</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: Marine Grade A"
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="changeDate"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>변경일자</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="YYYYMMDD"
+ maxLength={8}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
</div>
<DialogFooter>
diff --git a/lib/items/table/import-excel-button.tsx b/lib/items/table/import-excel-button.tsx
index 845f5fe2..7509143b 100644
--- a/lib/items/table/import-excel-button.tsx
+++ b/lib/items/table/import-excel-button.tsx
@@ -87,7 +87,7 @@ export function ImportItemButton({ onSuccess }: ImportItemButtonProps) {
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 === "itemCode" || v === "item_code")) {
headerRowIndex = rowNumber;
headerRow = row;
headerValues = [...values];
@@ -106,17 +106,25 @@ export function ImportItemButton({ onSuccess }: ImportItemButtonProps) {
}
});
- // 필수 헤더 확인
- const requiredHeaders = ["아이템 코드", "아이템 명", "설명"];
- const alternativeHeaders = {
- "아이템 코드": ["itemCode", "item_code"],
- "아이템 명": ["itemName", "item_name"],
- "설명": ["description"]
+ // 필수 헤더 확인 (새로운 컬럼 구조에 맞게 수정)
+ const requiredHeaders = ["자재그룹코드", "자재그룹명"];
+ const alternativeHeaders: Record<string, string[]> = {
+ "자재그룹코드": ["itemCode", "item_code"],
+ "자재그룹명": ["itemName", "item_name"],
+ "상세": ["description"],
+ "부모 아이템 코드": ["parentItemCode", "parent_item_code"],
+ "레벨": ["itemLevel", "item_level"],
+ "삭제 플래그": ["deleteFlag", "delete_flag"],
+ "단위": ["unitOfMeasure", "unit_of_measure"],
+ "강종": ["steelType", "steel_type"],
+ "등급 재질": ["gradeMaterial", "grade_material"],
+ "변경일자": ["changeDate", "change_date"],
+ "기본단위": ["baseUnitOfMeasure", "base_unit_of_measure"]
};
// 헤더 매핑 확인 (대체 이름 포함)
const missingHeaders = requiredHeaders.filter(header => {
- const alternatives = alternativeHeaders[header as keyof typeof alternativeHeaders] || [];
+ const alternatives = alternativeHeaders[header] || [];
return !(header in headerMapping) &&
!alternatives.some(alt => alt in headerMapping);
});
@@ -126,11 +134,11 @@ export function ImportItemButton({ onSuccess }: ImportItemButtonProps) {
}
// 데이터 행 추출 (헤더 이후 행부터)
- const dataRows: Record<string, any>[] = [];
+ const dataRows: Record<string, unknown>[] = [];
worksheet.eachRow((row, rowNumber) => {
if (rowNumber > headerRowIndex) {
- const rowData: Record<string, any> = {};
+ const rowData: Record<string, unknown> = {};
const values = row.values as (string | null | undefined)[];
// 헤더 매핑에 따라 데이터 추출
diff --git a/lib/items/table/import-item-handler.tsx b/lib/items/table/import-item-handler.tsx
index 541d6fe1..170ba24c 100644
--- a/lib/items/table/import-item-handler.tsx
+++ b/lib/items/table/import-item-handler.tsx
@@ -5,9 +5,17 @@ import { createItem } from "../service" // 아이템 생성 서버 액션
// 아이템 데이터 검증을 위한 Zod 스키마
const itemSchema = z.object({
- itemCode: z.string().min(1, "아이템 코드는 필수입니다"),
- itemName: z.string().min(1, "아이템 명은 필수입니다"),
+ itemCode: z.string().min(1, "자재그룹코드는 필수입니다"),
+ itemName: z.string().min(1, "자재그룹명은 필수입니다"),
description: z.string().nullable().optional(),
+ parentItemCode: z.string().max(18).nullable().optional(),
+ itemLevel: z.number().int().min(1).max(5).nullable().optional(),
+ deleteFlag: z.string().max(1).nullable().optional(),
+ unitOfMeasure: z.string().max(3).nullable().optional(),
+ steelType: z.string().max(2).nullable().optional(),
+ gradeMaterial: z.string().max(50).nullable().optional(),
+ changeDate: z.string().max(8).nullable().optional(),
+ baseUnitOfMeasure: z.string().max(3).nullable().optional(),
});
interface ProcessResult {
@@ -20,7 +28,7 @@ interface ProcessResult {
* Excel 파일에서 가져온 아이템 데이터 처리하는 함수
*/
export async function processFileImport(
- jsonData: any[],
+ jsonData: Record<string, unknown>[],
progressCallback?: (current: number, total: number) => void
): Promise<ProcessResult> {
// 결과 카운터 초기화
@@ -54,15 +62,31 @@ export async function processFileImport(
try {
// 필드 매핑 (한글/영문 필드명 모두 지원)
- const itemCode = row["아이템 코드"] || row["itemCode"] || row["item_code"] || "";
- const itemName = row["아이템 명"] || row["itemName"] || row["item_name"] || "";
- const description = row["설명"] || row["description"] || null;
+ const itemCode = row["자재그룹코드"] || row["itemCode"] || row["item_code"] || "";
+ const itemName = row["자재그룹명"] || row["itemName"] || row["item_name"] || "";
+ const description = row["상세"] || row["description"] || null;
+ const parentItemCode = row["부모 아이템 코드"] || row["parentItemCode"] || row["parent_item_code"] || null;
+ const itemLevel = row["레벨"] || row["itemLevel"] || row["item_level"] || null;
+ const deleteFlag = row["삭제 플래그"] || row["deleteFlag"] || row["delete_flag"] || null;
+ const unitOfMeasure = row["단위"] || row["unitOfMeasure"] || row["unit_of_measure"] || null;
+ const steelType = row["강종"] || row["steelType"] || row["steel_type"] || null;
+ const gradeMaterial = row["등급 재질"] || row["gradeMaterial"] || row["grade_material"] || null;
+ const changeDate = row["변경일자"] || row["changeDate"] || row["change_date"] || null;
+ const baseUnitOfMeasure = row["기본단위"] || row["baseUnitOfMeasure"] || row["base_unit_of_measure"] || null;
// 데이터 정제
const cleanedRow = {
itemCode: typeof itemCode === 'string' ? itemCode.trim() : String(itemCode).trim(),
itemName: typeof itemName === 'string' ? itemName.trim() : String(itemName).trim(),
description: description ? (typeof description === 'string' ? description : String(description)) : null,
+ parentItemCode: parentItemCode ? (typeof parentItemCode === 'string' ? parentItemCode.trim() : String(parentItemCode).trim()) : null,
+ itemLevel: itemLevel ? (typeof itemLevel === 'number' ? itemLevel : parseInt(String(itemLevel))) : null,
+ deleteFlag: deleteFlag ? (typeof deleteFlag === 'string' ? deleteFlag.trim() : String(deleteFlag).trim()) : null,
+ unitOfMeasure: unitOfMeasure ? (typeof unitOfMeasure === 'string' ? unitOfMeasure.trim() : String(unitOfMeasure).trim()) : null,
+ steelType: steelType ? (typeof steelType === 'string' ? steelType.trim() : String(steelType).trim()) : null,
+ gradeMaterial: gradeMaterial ? (typeof gradeMaterial === 'string' ? gradeMaterial.trim() : String(gradeMaterial).trim()) : null,
+ changeDate: changeDate ? (typeof changeDate === 'string' ? changeDate.trim() : String(changeDate).trim()) : null,
+ baseUnitOfMeasure: baseUnitOfMeasure ? (typeof baseUnitOfMeasure === 'string' ? baseUnitOfMeasure.trim() : String(baseUnitOfMeasure).trim()) : null,
};
// 데이터 유효성 검사
@@ -83,6 +107,14 @@ export async function processFileImport(
itemCode: cleanedRow.itemCode,
itemName: cleanedRow.itemName,
description: cleanedRow.description,
+ parentItemCode: cleanedRow.parentItemCode,
+ itemLevel: cleanedRow.itemLevel,
+ deleteFlag: cleanedRow.deleteFlag,
+ unitOfMeasure: cleanedRow.unitOfMeasure,
+ steelType: cleanedRow.steelType,
+ gradeMaterial: cleanedRow.gradeMaterial,
+ changeDate: cleanedRow.changeDate,
+ baseUnitOfMeasure: cleanedRow.baseUnitOfMeasure,
});
if (result.success || !result.error) {
diff --git a/lib/items/table/item-excel-template.tsx b/lib/items/table/item-excel-template.tsx
index 75338168..7d18bf03 100644
--- a/lib/items/table/item-excel-template.tsx
+++ b/lib/items/table/item-excel-template.tsx
@@ -15,9 +15,17 @@ export async function exportItemTemplate() {
// 컬럼 헤더 정의 및 스타일 적용
worksheet.columns = [
- { header: '아이템 코드', key: 'itemCode', width: 15 },
- { header: '아이템 명', key: 'itemName', width: 30 },
- { header: '설명', key: 'description', width: 50 }
+ { header: '자재그룹코드', key: 'itemCode', width: 15 },
+ { header: '자재그룹명', key: 'itemName', width: 30 },
+ { header: '상세', key: 'description', width: 50 },
+ { header: '부모 아이템 코드', key: 'parentItemCode', width: 18 },
+ { header: '레벨', key: 'itemLevel', width: 10 },
+ { header: '삭제 플래그', key: 'deleteFlag', width: 12 },
+ { header: '단위', key: 'unitOfMeasure', width: 10 },
+ { header: '강종', key: 'steelType', width: 10 },
+ { header: '등급 재질', key: 'gradeMaterial', width: 20 },
+ { header: '변경일자', key: 'changeDate', width: 12 },
+ { header: '기본단위', key: 'baseUnitOfMeasure', width: 12 }
];
// 헤더 스타일 적용
@@ -42,8 +50,32 @@ export async function exportItemTemplate() {
// 샘플 데이터 추가
const sampleData = [
- { itemCode: 'ITEM001', itemName: '샘플 아이템 1', description: '이것은 샘플 아이템 1의 설명입니다.' },
- { itemCode: 'ITEM002', itemName: '샘플 아이템 2', description: '이것은 샘플 아이템 2의 설명입니다.' }
+ {
+ itemCode: 'SAMPLE001',
+ itemName: '샘플1',
+ description: '샘플1',
+ parentItemCode: null,
+ itemLevel: 1,
+ deleteFlag: 'N',
+ unitOfMeasure: 'EA',
+ steelType: 'SS',
+ gradeMaterial: 'gradeMaterial',
+ changeDate: '20241201',
+ baseUnitOfMeasure: 'EA'
+ },
+ {
+ itemCode: 'SAMPLE002',
+ itemName: '샘플2',
+ description: '샘플2',
+ parentItemCode: 'SAMPLE001',
+ itemLevel: 2,
+ deleteFlag: 'N',
+ unitOfMeasure: 'EA',
+ steelType: 'SS',
+ gradeMaterial: 'gradeMaterial',
+ changeDate: '20241201',
+ baseUnitOfMeasure: 'EA'
+ }
];
// 데이터 행 추가
diff --git a/lib/items/table/items-table.tsx b/lib/items/table/items-table.tsx
index bbbafc2f..2bc1c913 100644
--- a/lib/items/table/items-table.tsx
+++ b/lib/items/table/items-table.tsx
@@ -74,17 +74,58 @@ export function ItemsTable({ promises }: ItemsTableProps) {
*/
const advancedFilterFields: DataTableAdvancedFilterField<Item>[] = [
{
+ id: "itemLevel",
+ label: "레벨",
+ type: "number",
+ },
+ {
id: "itemCode",
- label: "Item Code",
+ label: "자재그룹코드",
type: "text",
},
{
id: "itemName",
- label: "Item Name",
+ label: "자재그룹이름",
type: "text",
- }, {
+ },
+ {
id: "description",
- label: "Description",
+ label: "상세",
+ type: "text",
+ },
+ {
+ id: "parentItemCode",
+ label: "부모 아이템 코드",
+ type: "text",
+ },
+ {
+ id: "deleteFlag",
+ label: "삭제 플래그",
+ type: "text",
+ },
+ {
+ id: "unitOfMeasure",
+ label: "단위",
+ type: "text",
+ },
+ {
+ id: "steelType",
+ label: "강종",
+ type: "text",
+ },
+ {
+ id: "gradeMaterial",
+ label: "등급 재질",
+ type: "text",
+ },
+ {
+ id: "changeDate",
+ label: "변경일자",
+ type: "text",
+ },
+ {
+ id: "baseUnitOfMeasure",
+ label: "기본단위",
type: "text",
},
]
diff --git a/lib/items/table/update-item-sheet.tsx b/lib/items/table/update-item-sheet.tsx
index 4bcdbfcb..fcdc0a70 100644
--- a/lib/items/table/update-item-sheet.tsx
+++ b/lib/items/table/update-item-sheet.tsx
@@ -1,7 +1,6 @@
"use client"
import * as React from "react"
-import { tasks, type Task } from "@/db/schema/tasks"
import { zodResolver } from "@hookform/resolvers/zod"
import { Loader } from "lucide-react"
import { useForm } from "react-hook-form"
@@ -19,7 +18,6 @@ import {
import {
Select,
SelectContent,
- SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
@@ -35,8 +33,6 @@ import {
} from "@/components/ui/sheet"
import { Textarea } from "@/components/ui/textarea"
-import { modifiTask } from "@/lib//tasks/service"
-import { updateTaskSchema, type UpdateTaskSchema } from "@/lib/tasks/validations"
import { Item } from "@/db/schema/items"
import { updateItemSchema, UpdateItemSchema } from "../validations"
import { modifyItem } from "../service"
@@ -57,7 +53,14 @@ export function UpdateItemSheet({ item, ...props }: UpdateItemSheetProps) {
itemCode: item?.itemCode ?? "",
itemName: item?.itemName ?? "",
description: item?.description ?? "",
-
+ parentItemCode: item?.parentItemCode ?? "",
+ itemLevel: item?.itemLevel ?? null,
+ deleteFlag: item?.deleteFlag ?? "",
+ unitOfMeasure: item?.unitOfMeasure ?? "",
+ steelType: item?.steelType ?? "",
+ gradeMaterial: item?.gradeMaterial ?? "",
+ changeDate: item?.changeDate ?? "",
+ baseUnitOfMeasure: item?.baseUnitOfMeasure ?? "",
},
})
@@ -68,6 +71,14 @@ export function UpdateItemSheet({ item, ...props }: UpdateItemSheetProps) {
itemCode: item.itemCode ?? "",
itemName: item.itemName ?? "",
description: item.description ?? "",
+ parentItemCode: item.parentItemCode ?? "",
+ itemLevel: item.itemLevel ?? null,
+ deleteFlag: item.deleteFlag ?? "",
+ unitOfMeasure: item.unitOfMeasure ?? "",
+ steelType: item.steelType ?? "",
+ gradeMaterial: item.gradeMaterial ?? "",
+ changeDate: item.changeDate ?? "",
+ baseUnitOfMeasure: item.baseUnitOfMeasure ?? "",
});
}
}, [item, form]);
@@ -111,10 +122,9 @@ export function UpdateItemSheet({ item, ...props }: UpdateItemSheetProps) {
name="itemCode"
render={({ field }) => (
<FormItem>
- <FormLabel>Item Code</FormLabel>
+ <FormLabel>자재그룹코드</FormLabel>
<FormControl>
- <Input placeholder="e.g." {...field} />
-
+ <Input placeholder="예: BOLT001" {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -125,10 +135,10 @@ export function UpdateItemSheet({ item, ...props }: UpdateItemSheetProps) {
name="itemName"
render={({ field }) => (
<FormItem>
- <FormLabel>Item Name</FormLabel>
+ <FormLabel>자재그룹명</FormLabel>
<FormControl>
<Input
- placeholder="e.g."
+ placeholder="예: 육각볼트 M8x20"
{...field}
/>
</FormControl>
@@ -142,10 +152,10 @@ export function UpdateItemSheet({ item, ...props }: UpdateItemSheetProps) {
name="description"
render={({ field }) => (
<FormItem>
- <FormLabel>Description</FormLabel>
+ <FormLabel>상세</FormLabel>
<FormControl>
<Textarea
- placeholder="e.g."
+ placeholder="예: 표준 육각 볼트 스테인리스 스틸"
{...field}
/>
</FormControl>
@@ -154,6 +164,171 @@ export function UpdateItemSheet({ item, ...props }: UpdateItemSheetProps) {
)}
/>
+ {/* 2열 레이아웃 */}
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="itemLevel"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>레벨</FormLabel>
+ <FormControl>
+ <Input
+ type="number"
+ min="1"
+ max="5"
+ placeholder="1-5"
+ {...field}
+ value={field.value ?? ""}
+ onChange={(e) => field.onChange(e.target.value ? parseInt(e.target.value) : null)}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="deleteFlag"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>삭제 플래그</FormLabel>
+ <FormControl>
+ <Select onValueChange={field.onChange} value={field.value || ""}>
+ <SelectTrigger>
+ <SelectValue placeholder="선택" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="N">N (사용)</SelectItem>
+ <SelectItem value="Y">Y (삭제)</SelectItem>
+ </SelectContent>
+ </Select>
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="parentItemCode"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>부모 아이템 코드</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: BOLT001"
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="steelType"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>강종</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: SS, CS, AL"
+ maxLength={2}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="unitOfMeasure"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>단위</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: EA, SET, KG"
+ maxLength={3}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="baseUnitOfMeasure"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>기본단위</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: EA, SET, KG"
+ maxLength={3}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <FormField
+ control={form.control}
+ name="gradeMaterial"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>등급 재질</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="예: Marine Grade A"
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
+ name="changeDate"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>변경일자</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="YYYYMMDD"
+ maxLength={8}
+ {...field}
+ value={field.value || ""}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
<SheetFooter className="gap-2 pt-2 sm:space-x-0">
<SheetClose asChild>
<Button type="button" variant="outline">
diff --git a/lib/items/validations.ts b/lib/items/validations.ts
index d299959c..14fc27b1 100644
--- a/lib/items/validations.ts
+++ b/lib/items/validations.ts
@@ -22,6 +22,14 @@ export const searchParamsCache = createSearchParamsCache({
itemCode: parseAsString.withDefault(""),
itemName: parseAsString.withDefault(""),
description: parseAsString.withDefault(""),
+ parentItemCode: parseAsString.withDefault(""),
+ itemLevel: parseAsInteger.withDefault(5),
+ deleteFlag: parseAsString.withDefault(""),
+ unitOfMeasure: parseAsString.withDefault(""),
+ steelType: parseAsString.withDefault(""),
+ gradeMaterial: parseAsString.withDefault(""),
+ changeDate: parseAsString.withDefault(""),
+ baseUnitOfMeasure: parseAsString.withDefault(""),
// advanced filter
filters: getFiltersStateParser().withDefault([]),
@@ -31,15 +39,31 @@ export const searchParamsCache = createSearchParamsCache({
})
export const createItemSchema = z.object({
- itemCode: z.string(),
- itemName: z.string(),
- description: z.string(),
+ itemCode: z.string().min(1, "아이템 코드는 필수입니다"),
+ itemName: z.string().min(1, "아이템명은 필수입니다"),
+ description: z.string().nullable().optional(),
+ parentItemCode: z.string().max(18).nullable().optional(),
+ itemLevel: z.number().int().min(1).max(5).nullable().optional(),
+ deleteFlag: z.string().max(1).nullable().optional(),
+ unitOfMeasure: z.string().max(3).nullable().optional(),
+ steelType: z.string().max(2).nullable().optional(),
+ gradeMaterial: z.string().max(50).nullable().optional(),
+ changeDate: z.string().max(8).nullable().optional(),
+ baseUnitOfMeasure: z.string().max(3).nullable().optional(),
})
export const updateItemSchema = z.object({
itemCode: z.string().optional(),
itemName: z.string().optional(),
description: z.string().optional(),
+ parentItemCode: z.string().max(18).nullable().optional(),
+ itemLevel: z.number().int().min(1).max(5).nullable().optional(),
+ deleteFlag: z.string().max(1).nullable().optional(),
+ unitOfMeasure: z.string().max(3).nullable().optional(),
+ steelType: z.string().max(2).nullable().optional(),
+ gradeMaterial: z.string().max(50).nullable().optional(),
+ changeDate: z.string().max(8).nullable().optional(),
+ baseUnitOfMeasure: z.string().max(3).nullable().optional(),
})
export type GetItemsSchema = Awaited<ReturnType<typeof searchParamsCache.parse>>