- 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>
263 lines
11 KiB
TypeScript
263 lines
11 KiB
TypeScript
'use client';
|
|
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { Badge } from '@/components/ui/badge';
|
|
|
|
const INPUT_TYPE_OPTIONS = [
|
|
{ value: 'textbox', label: '텍스트 입력' },
|
|
{ value: 'number', label: '숫자 입력' },
|
|
{ value: 'dropdown', label: '드롭다운' },
|
|
{ value: 'checkbox', label: '체크박스' },
|
|
{ value: 'date', label: '날짜' },
|
|
{ value: 'textarea', label: '긴 텍스트' },
|
|
];
|
|
|
|
interface MasterFieldDialogProps {
|
|
isMasterFieldDialogOpen: boolean;
|
|
setIsMasterFieldDialogOpen: (open: boolean) => void;
|
|
editingMasterFieldId: number | null;
|
|
setEditingMasterFieldId: (id: number | null) => void;
|
|
newMasterFieldName: string;
|
|
setNewMasterFieldName: (name: string) => void;
|
|
newMasterFieldKey: string;
|
|
setNewMasterFieldKey: (key: string) => void;
|
|
newMasterFieldInputType: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
|
|
setNewMasterFieldInputType: (type: any) => void;
|
|
newMasterFieldRequired: boolean;
|
|
setNewMasterFieldRequired: (required: boolean) => void;
|
|
newMasterFieldCategory: string;
|
|
setNewMasterFieldCategory: (category: string) => void;
|
|
newMasterFieldDescription: string;
|
|
setNewMasterFieldDescription: (description: string) => void;
|
|
newMasterFieldOptions: string;
|
|
setNewMasterFieldOptions: (options: string) => void;
|
|
newMasterFieldAttributeType: 'custom' | 'unit' | 'material' | 'surface';
|
|
setNewMasterFieldAttributeType: (type: 'custom' | 'unit' | 'material' | 'surface') => void;
|
|
newMasterFieldMultiColumn: boolean;
|
|
setNewMasterFieldMultiColumn: (multi: boolean) => void;
|
|
newMasterFieldColumnCount: number;
|
|
setNewMasterFieldColumnCount: (count: number) => void;
|
|
newMasterFieldColumnNames: string[];
|
|
setNewMasterFieldColumnNames: (names: string[]) => void;
|
|
handleUpdateMasterField: () => void;
|
|
handleAddMasterField: () => void;
|
|
}
|
|
|
|
export function MasterFieldDialog({
|
|
isMasterFieldDialogOpen,
|
|
setIsMasterFieldDialogOpen,
|
|
editingMasterFieldId,
|
|
setEditingMasterFieldId,
|
|
newMasterFieldName,
|
|
setNewMasterFieldName,
|
|
newMasterFieldKey,
|
|
setNewMasterFieldKey,
|
|
newMasterFieldInputType,
|
|
setNewMasterFieldInputType,
|
|
newMasterFieldRequired,
|
|
setNewMasterFieldRequired,
|
|
newMasterFieldCategory,
|
|
setNewMasterFieldCategory,
|
|
newMasterFieldDescription,
|
|
setNewMasterFieldDescription,
|
|
newMasterFieldOptions,
|
|
setNewMasterFieldOptions,
|
|
newMasterFieldAttributeType,
|
|
setNewMasterFieldAttributeType,
|
|
newMasterFieldMultiColumn,
|
|
setNewMasterFieldMultiColumn,
|
|
newMasterFieldColumnCount,
|
|
setNewMasterFieldColumnCount,
|
|
newMasterFieldColumnNames,
|
|
setNewMasterFieldColumnNames,
|
|
handleUpdateMasterField,
|
|
handleAddMasterField,
|
|
}: MasterFieldDialogProps) {
|
|
return (
|
|
<Dialog open={isMasterFieldDialogOpen} onOpenChange={(open) => {
|
|
setIsMasterFieldDialogOpen(open);
|
|
if (!open) {
|
|
setEditingMasterFieldId(null);
|
|
setNewMasterFieldName('');
|
|
setNewMasterFieldKey('');
|
|
setNewMasterFieldInputType('textbox');
|
|
setNewMasterFieldRequired(false);
|
|
setNewMasterFieldCategory('공통');
|
|
setNewMasterFieldDescription('');
|
|
setNewMasterFieldOptions('');
|
|
setNewMasterFieldAttributeType('custom');
|
|
setNewMasterFieldMultiColumn(false);
|
|
setNewMasterFieldColumnCount(2);
|
|
setNewMasterFieldColumnNames(['컬럼1', '컬럼2']);
|
|
}
|
|
}}>
|
|
<DialogContent className="max-w-[95vw] sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>{editingMasterFieldId ? '마스터 항목 수정' : '마스터 항목 추가'}</DialogTitle>
|
|
<DialogDescription>
|
|
여러 섹션에서 재사용할 수 있는 항목 템플릿을 생성합니다
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="space-y-4">
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<Label>항목명 *</Label>
|
|
<Input
|
|
value={newMasterFieldName}
|
|
onChange={(e) => setNewMasterFieldName(e.target.value)}
|
|
placeholder="예: 품목명"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label>필드 키 *</Label>
|
|
<Input
|
|
value={newMasterFieldKey}
|
|
onChange={(e) => setNewMasterFieldKey(e.target.value)}
|
|
placeholder="예: itemName"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label>입력방식 *</Label>
|
|
<Select value={newMasterFieldInputType} onValueChange={(v: any) => setNewMasterFieldInputType(v)}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{INPUT_TYPE_OPTIONS.map(opt => (
|
|
<SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<Switch checked={newMasterFieldRequired} onCheckedChange={setNewMasterFieldRequired} />
|
|
<Label>필수 항목</Label>
|
|
</div>
|
|
|
|
<div>
|
|
<Label>설명</Label>
|
|
<Textarea
|
|
value={newMasterFieldDescription}
|
|
onChange={(e) => setNewMasterFieldDescription(e.target.value)}
|
|
placeholder="항목에 대한 설명"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-1">* 제품 유형에 따라 품목 분류를 표시 [필수]</p>
|
|
</div>
|
|
|
|
{(newMasterFieldInputType === 'textbox' || newMasterFieldInputType === 'textarea') && (
|
|
<div className="space-y-4 p-4 border rounded-lg bg-gray-50">
|
|
<div className="flex items-center gap-2">
|
|
<Switch
|
|
checked={newMasterFieldMultiColumn}
|
|
onCheckedChange={(checked) => {
|
|
setNewMasterFieldMultiColumn(checked);
|
|
if (!checked) {
|
|
setNewMasterFieldColumnCount(2);
|
|
setNewMasterFieldColumnNames(['컬럼1', '컬럼2']);
|
|
}
|
|
}}
|
|
/>
|
|
<Label>다중 컬럼 사용</Label>
|
|
</div>
|
|
<p className="text-xs text-gray-500">
|
|
활성화하면 한 항목에 여러 개의 값을 입력받을 수 있습니다 (예: 규격 - 가로, 세로, 높이)
|
|
</p>
|
|
|
|
{newMasterFieldMultiColumn && (
|
|
<div className="space-y-4 pt-4 border-t">
|
|
<div>
|
|
<Label>컬럼 개수</Label>
|
|
<Input
|
|
type="number"
|
|
min="2"
|
|
max="10"
|
|
value={newMasterFieldColumnCount}
|
|
onChange={(e) => {
|
|
const count = parseInt(e.target.value) || 2;
|
|
setNewMasterFieldColumnCount(count);
|
|
// 컬럼 개수에 맞게 이름 배열 조정
|
|
const newNames = Array.from({ length: count }, (_, i) =>
|
|
newMasterFieldColumnNames[i] || `컬럼${i + 1}`
|
|
);
|
|
setNewMasterFieldColumnNames(newNames);
|
|
}}
|
|
placeholder="컬럼 개수 (2~10)"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<Label>컬럼 이름 설정</Label>
|
|
<div className="space-y-2 mt-2">
|
|
{Array.from({ length: newMasterFieldColumnCount }, (_, i) => (
|
|
<Input
|
|
key={i}
|
|
value={newMasterFieldColumnNames[i] || ''}
|
|
onChange={(e) => {
|
|
const newNames = [...newMasterFieldColumnNames];
|
|
newNames[i] = e.target.value;
|
|
setNewMasterFieldColumnNames(newNames);
|
|
}}
|
|
placeholder={`${i + 1}번째 컬럼 이름`}
|
|
/>
|
|
))}
|
|
</div>
|
|
<p className="text-xs text-gray-500 mt-2">
|
|
예시: 가로, 세로, 높이 / 최소값, 최대값 / 상한, 하한
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{newMasterFieldInputType === 'dropdown' && (
|
|
<div className="space-y-4">
|
|
<div>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<Label>드롭다운 옵션</Label>
|
|
{newMasterFieldAttributeType !== 'custom' && (
|
|
<Badge variant="secondary" className="text-xs">
|
|
{newMasterFieldAttributeType === 'unit' ? '단위' :
|
|
newMasterFieldAttributeType === 'material' ? '재질' : '표면처리'} 연동
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<Textarea
|
|
value={newMasterFieldOptions}
|
|
onChange={(e) => setNewMasterFieldOptions(e.target.value)}
|
|
placeholder="제품,부품,원자재 (쉼표로 구분)"
|
|
disabled={newMasterFieldAttributeType !== 'custom'}
|
|
className="min-h-[80px]"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
{newMasterFieldAttributeType === 'custom'
|
|
? '쉼표(,)로 구분하여 입력하세요'
|
|
: '속성 탭에서 옵션을 추가/삭제하면 자동으로 반영됩니다'
|
|
}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => setIsMasterFieldDialogOpen(false)}>취소</Button>
|
|
<Button onClick={editingMasterFieldId ? handleUpdateMasterField : handleAddMasterField}>
|
|
{editingMasterFieldId ? '수정' : '추가'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|