[feat]: Item Master 데이터 관리 기능 구현 및 타입 에러 수정
- ItemMasterDataManagement 컴포넌트 구조화 (tabs, dialogs, components 분리) - HierarchyTab 타입 에러 수정 (BOMItem section_id, updated_at 추가) - API 클라이언트 구현 (item-master.ts, 13개 엔드포인트) - ItemMasterContext 구현 (상태 관리 및 데이터 흐름) - 백엔드 요구사항 문서 작성 (CORS 설정, API 스펙 등) - SSR 호환성 수정 (navigator API typeof window 체크) - 미사용 변수 ESLint 에러 해결 - Context 리팩토링 (AuthContext, RootProvider 추가) - API 유틸리티 추가 (error-handler, logger, transformers) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,25 +10,15 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Plus, Edit, Trash2, Package, GripVertical } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export interface BOMItem {
|
||||
id: string;
|
||||
itemCode: string;
|
||||
itemName: string;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
itemType?: string;
|
||||
note?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
import type { BOMItem } from '@/contexts/ItemMasterContext';
|
||||
|
||||
interface BOMManagementSectionProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
bomItems: BOMItem[];
|
||||
onAddItem: (item: Omit<BOMItem, 'id' | 'createdAt'>) => void;
|
||||
onUpdateItem: (id: string, item: Partial<BOMItem>) => void;
|
||||
onDeleteItem: (id: string) => void;
|
||||
onAddItem: (item: Omit<BOMItem, 'id' | 'createdAt' | 'created_at' | 'updated_at' | 'tenant_id' | 'section_id'>) => void;
|
||||
onUpdateItem: (id: number, item: Partial<BOMItem>) => void;
|
||||
onDeleteItem: (id: number) => void;
|
||||
itemTypeOptions?: { value: string; label: string }[];
|
||||
unitOptions?: { value: string; label: string }[];
|
||||
}
|
||||
@@ -53,7 +43,7 @@ export function BOMManagementSection({
|
||||
],
|
||||
}: BOMManagementSectionProps) {
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [itemCode, setItemCode] = useState('');
|
||||
const [itemName, setItemName] = useState('');
|
||||
const [quantity, setQuantity] = useState('1');
|
||||
@@ -64,12 +54,12 @@ export function BOMManagementSection({
|
||||
const handleOpenDialog = (item?: BOMItem) => {
|
||||
if (item) {
|
||||
setEditingId(item.id);
|
||||
setItemCode(item.itemCode);
|
||||
setItemName(item.itemName);
|
||||
setItemCode(item.item_code || '');
|
||||
setItemName(item.item_name);
|
||||
setQuantity(item.quantity.toString());
|
||||
setUnit(item.unit);
|
||||
setItemType(item.itemType || 'part');
|
||||
setNote(item.note || '');
|
||||
setUnit(item.unit || 'EA');
|
||||
setItemType('part');
|
||||
setNote(item.spec || '');
|
||||
} else {
|
||||
setEditingId(null);
|
||||
setItemCode('');
|
||||
@@ -93,12 +83,11 @@ export function BOMManagementSection({
|
||||
}
|
||||
|
||||
const itemData = {
|
||||
itemCode,
|
||||
itemName,
|
||||
item_code: itemCode,
|
||||
item_name: itemName,
|
||||
quantity: qty,
|
||||
unit,
|
||||
itemType,
|
||||
note: note.trim() || undefined,
|
||||
spec: note.trim() || undefined,
|
||||
};
|
||||
|
||||
if (editingId) {
|
||||
@@ -112,7 +101,7 @@ export function BOMManagementSection({
|
||||
setIsDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
const handleDelete = (id: number) => {
|
||||
if (confirm('이 BOM 품목을 삭제하시겠습니까?')) {
|
||||
onDeleteItem(id);
|
||||
toast.success('BOM 품목이 삭제되었습니다');
|
||||
@@ -159,19 +148,16 @@ export function BOMManagementSection({
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<GripVertical className="h-4 w-4 text-gray-400" />
|
||||
<span className="font-medium">{item.itemName}</span>
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{item.itemCode}
|
||||
</Badge>
|
||||
{item.itemType && (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{itemTypeOptions.find((t) => t.value === item.itemType)?.label || item.itemType}
|
||||
<span className="font-medium">{item.item_name}</span>
|
||||
{item.item_code && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{item.item_code}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-6 text-sm text-gray-500 mt-1">
|
||||
수량: {item.quantity} {item.unit}
|
||||
{item.note && <span className="ml-2">• {item.note}</span>}
|
||||
수량: {item.quantity} {item.unit || 'EA'}
|
||||
{item.spec && <span className="ml-2">• {item.spec}</span>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
|
||||
Reference in New Issue
Block a user