Files
sam-react-prod/src/components/items/ItemMasterDataManagement/tabs/MasterFieldTab/index.tsx
byeongcheolryu df3db155dd [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>
2025-11-23 16:10:27 +09:00

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>
);
}