[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:
byeongcheolryu
2025-11-23 16:10:27 +09:00
parent 63f5df7d7d
commit df3db155dd
69 changed files with 31467 additions and 4796 deletions

View File

@@ -0,0 +1,212 @@
'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';
const INPUT_TYPE_OPTIONS = [
{ value: 'textbox', label: '텍스트 입력' },
{ value: 'number', label: '숫자 입력' },
{ value: 'dropdown', label: '드롭다운' },
{ value: 'checkbox', label: '체크박스' },
{ value: 'date', label: '날짜' },
{ value: 'textarea', label: '긴 텍스트' },
];
interface TemplateFieldDialogProps {
isTemplateFieldDialogOpen: boolean;
setIsTemplateFieldDialogOpen: (open: boolean) => void;
editingTemplateFieldId: number | null;
setEditingTemplateFieldId: (id: number | null) => void;
templateFieldName: string;
setTemplateFieldName: (name: string) => void;
templateFieldKey: string;
setTemplateFieldKey: (key: string) => void;
templateFieldInputType: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
setTemplateFieldInputType: (type: any) => void;
templateFieldRequired: boolean;
setTemplateFieldRequired: (required: boolean) => void;
templateFieldOptions: string;
setTemplateFieldOptions: (options: string) => void;
templateFieldDescription: string;
setTemplateFieldDescription: (description: string) => void;
templateFieldMultiColumn: boolean;
setTemplateFieldMultiColumn: (multi: boolean) => void;
templateFieldColumnCount: number;
setTemplateFieldColumnCount: (count: number) => void;
templateFieldColumnNames: string[];
setTemplateFieldColumnNames: (names: string[]) => void;
handleAddTemplateField: () => void;
}
export function TemplateFieldDialog({
isTemplateFieldDialogOpen,
setIsTemplateFieldDialogOpen,
editingTemplateFieldId,
setEditingTemplateFieldId,
templateFieldName,
setTemplateFieldName,
templateFieldKey,
setTemplateFieldKey,
templateFieldInputType,
setTemplateFieldInputType,
templateFieldRequired,
setTemplateFieldRequired,
templateFieldOptions,
setTemplateFieldOptions,
templateFieldDescription,
setTemplateFieldDescription,
templateFieldMultiColumn,
setTemplateFieldMultiColumn,
templateFieldColumnCount,
setTemplateFieldColumnCount,
templateFieldColumnNames,
setTemplateFieldColumnNames,
handleAddTemplateField,
}: TemplateFieldDialogProps) {
return (
<Dialog open={isTemplateFieldDialogOpen} onOpenChange={(open) => {
setIsTemplateFieldDialogOpen(open);
if (!open) {
setEditingTemplateFieldId(null);
setTemplateFieldName('');
setTemplateFieldKey('');
setTemplateFieldInputType('textbox');
setTemplateFieldRequired(false);
setTemplateFieldOptions('');
setTemplateFieldDescription('');
setTemplateFieldMultiColumn(false);
setTemplateFieldColumnCount(2);
setTemplateFieldColumnNames(['컬럼1', '컬럼2']);
}
}}>
<DialogContent className="max-w-[95vw] sm:max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{editingTemplateFieldId ? '항목 수정' : '항목 추가'}</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={templateFieldName}
onChange={(e) => setTemplateFieldName(e.target.value)}
placeholder="예: 품목명"
/>
</div>
<div>
<Label> *</Label>
<Input
value={templateFieldKey}
onChange={(e) => setTemplateFieldKey(e.target.value)}
placeholder="예: itemName"
/>
</div>
</div>
<div>
<Label> *</Label>
<Select value={templateFieldInputType} onValueChange={(v: any) => setTemplateFieldInputType(v)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{INPUT_TYPE_OPTIONS.map(opt => (
<SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
))}
</SelectContent>
</Select>
</div>
{templateFieldInputType === 'dropdown' && (
<div>
<Label> </Label>
<Input
value={templateFieldOptions}
onChange={(e) => setTemplateFieldOptions(e.target.value)}
placeholder="옵션1, 옵션2, 옵션3 (쉼표로 구분)"
/>
</div>
)}
{(templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') && (
<div className="space-y-3 border rounded-lg p-4 bg-muted/30">
<div className="flex items-center gap-2">
<Switch
checked={templateFieldMultiColumn}
onCheckedChange={setTemplateFieldMultiColumn}
/>
<Label> </Label>
</div>
{templateFieldMultiColumn && (
<>
<div>
<Label> </Label>
<Input
type="number"
min={2}
max={10}
value={templateFieldColumnCount}
onChange={(e) => {
const count = parseInt(e.target.value) || 2;
setTemplateFieldColumnCount(count);
const newNames = Array.from({ length: count }, (_, i) =>
templateFieldColumnNames[i] || `컬럼${i + 1}`
);
setTemplateFieldColumnNames(newNames);
}}
/>
</div>
<div className="space-y-2">
<Label></Label>
{Array.from({ length: templateFieldColumnCount }).map((_, idx) => (
<Input
key={idx}
placeholder={`컬럼 ${idx + 1}`}
value={templateFieldColumnNames[idx] || ''}
onChange={(e) => {
const newNames = [...templateFieldColumnNames];
newNames[idx] = e.target.value;
setTemplateFieldColumnNames(newNames);
}}
/>
))}
</div>
</>
)}
</div>
)}
<div>
<Label> ()</Label>
<Textarea
value={templateFieldDescription}
onChange={(e) => setTemplateFieldDescription(e.target.value)}
placeholder="항목에 대한 설명"
/>
</div>
<div className="flex items-center gap-2">
<Switch checked={templateFieldRequired} onCheckedChange={setTemplateFieldRequired} />
<Label> </Label>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsTemplateFieldDialogOpen(false)}></Button>
<Button onClick={handleAddTemplateField}>
{editingTemplateFieldId ? '수정' : '추가'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}