- 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>
135 lines
5.5 KiB
TypeScript
135 lines
5.5 KiB
TypeScript
import type { ItemMasterField } from '@/contexts/ItemMasterContext';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Plus, Edit, Trash2 } from 'lucide-react';
|
|
|
|
// 입력방식 옵션 (ItemMasterDataManagement에서 사용하는 상수)
|
|
const INPUT_TYPE_OPTIONS = [
|
|
{ value: 'textbox', label: '텍스트' },
|
|
{ value: 'dropdown', label: '드롭다운' },
|
|
{ value: 'checkbox', label: '체크박스' },
|
|
{ value: 'number', label: '숫자' },
|
|
{ value: 'date', label: '날짜' },
|
|
{ value: 'textarea', label: '텍스트영역' }
|
|
];
|
|
|
|
// 변경 레코드 타입 (임시 - 나중에 공통 타입으로 분리)
|
|
interface ChangeRecord {
|
|
masterFields: Array<{
|
|
type: 'add' | 'update' | 'delete';
|
|
id: string;
|
|
data?: any;
|
|
}>;
|
|
[key: string]: any;
|
|
}
|
|
|
|
interface MasterFieldTabProps {
|
|
itemMasterFields: ItemMasterField[];
|
|
setIsMasterFieldDialogOpen: (open: boolean) => void;
|
|
handleEditMasterField: (field: ItemMasterField) => void;
|
|
handleDeleteMasterField: (id: number) => void;
|
|
hasUnsavedChanges: boolean;
|
|
pendingChanges: ChangeRecord;
|
|
}
|
|
|
|
export function MasterFieldTab({
|
|
itemMasterFields,
|
|
setIsMasterFieldDialogOpen,
|
|
handleEditMasterField,
|
|
handleDeleteMasterField,
|
|
hasUnsavedChanges,
|
|
pendingChanges
|
|
}: MasterFieldTabProps) {
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div>
|
|
<CardTitle>마스터 항목 관리</CardTitle>
|
|
<CardDescription>재사용 가능한 항목 템플릿을 관리합니다</CardDescription>
|
|
</div>
|
|
{/* 변경사항 배지 - 나중에 사용 예정으로 임시 숨김 */}
|
|
{false && hasUnsavedChanges && pendingChanges.masterFields.length > 0 && (
|
|
<Badge variant="destructive" className="animate-pulse">
|
|
{pendingChanges.masterFields.length}개 변경
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<Button onClick={() => setIsMasterFieldDialogOpen(true)}>
|
|
<Plus className="h-4 w-4 mr-2" />항목 추가
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{itemMasterFields.length === 0 ? (
|
|
<div className="text-center py-8">
|
|
<p className="text-muted-foreground mb-2">등록된 마스터 항목이 없습니다</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
항목 추가 버튼을 눌러 재사용 가능한 항목을 등록하세요.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{itemMasterFields.map((field) => (
|
|
<div key={field.id} className="flex items-center justify-between p-4 border rounded hover:bg-gray-50 transition-colors">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2">
|
|
<span>{field.field_name}</span>
|
|
<Badge variant="outline" className="text-xs">
|
|
{INPUT_TYPE_OPTIONS.find(t => t.value === field.properties?.inputType)?.label}
|
|
</Badge>
|
|
{field.properties?.required && (
|
|
<Badge variant="destructive" className="text-xs">필수</Badge>
|
|
)}
|
|
<Badge variant="secondary" className="text-xs">{field.category}</Badge>
|
|
{(field.properties as any)?.attributeType && (field.properties as any).attributeType !== 'custom' && (
|
|
<Badge variant="default" className="text-xs bg-blue-500">
|
|
{(field.properties as any).attributeType === 'unit' ? '단위 연동' :
|
|
(field.properties as any).attributeType === 'material' ? '재질 연동' : '표면처리 연동'}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<div className="text-sm text-muted-foreground mt-1">
|
|
ID: {field.id}
|
|
{field.description && (
|
|
<span className="ml-2">• {field.description}</span>
|
|
)}
|
|
</div>
|
|
{field.properties?.options && field.properties.options.length > 0 && (
|
|
<div className="text-xs text-gray-500 mt-1">
|
|
옵션: {field.properties.options.join(', ')}
|
|
{(field.properties as any)?.attributeType && (field.properties as any).attributeType !== 'custom' && (
|
|
<span className="ml-2 text-blue-600">
|
|
(속성 탭 자동 동기화)
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={() => handleEditMasterField(field)}
|
|
>
|
|
<Edit className="h-4 w-4 text-blue-500" />
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={() => handleDeleteMasterField(field.id)}
|
|
>
|
|
<Trash2 className="h-4 w-4 text-red-500" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|