refactor: 품목기준관리 unused 코드 정리 및 import 최적화

- unused 변수 제거: _handleAddColumn, _handleDeleteColumn, _handleDeleteSectionWithTracking, _currentTemplateId, _itemSections
- unused import 제거: ItemMasterDialogs, Badge, toast
- unused helper 함수 제거: getInputTypeLabel
- 코드 감소: 968줄 → 954줄

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-12-24 15:26:38 +09:00
parent a823ae0777
commit 1664599cd5

View File

@@ -9,7 +9,7 @@ import { MasterFieldTab, HierarchyTab, SectionsTab } from './ItemMasterDataManag
import { LoadingSpinner } from '@/components/ui/loading-spinner';
import { ServerErrorPage } from '@/components/common/ServerErrorPage';
// 2025-12-24: Phase 2 UI 컴포넌트 분리
import { AttributeTabContent, ItemMasterDialogs } from './ItemMasterDataManagement/components';
import { AttributeTabContent } from './ItemMasterDataManagement/components';
import {
Database,
FileText,
@@ -17,8 +17,6 @@ import {
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
import { toast } from 'sonner';
// 커스텀 훅 import
import {
@@ -53,19 +51,6 @@ const INPUT_TYPE_OPTIONS = [
{ value: 'textarea', label: '텍스트영역' }
];
// 입력 타입 라벨 변환 헬퍼 함수 (중복 코드 제거)
const getInputTypeLabel = (inputType: string | undefined): string => {
const labels: Record<string, string> = {
textbox: '텍스트박스',
number: '숫자',
dropdown: '드롭다운',
checkbox: '체크박스',
date: '날짜',
textarea: '텍스트영역',
};
return labels[inputType || ''] || '텍스트박스';
};
export function ItemMasterDataManagement() {
const {
itemPages,
@@ -158,7 +143,6 @@ export function ItemMasterDataManagement() {
newColumnType, setNewColumnType,
newColumnRequired, setNewColumnRequired,
handleAddOption, handleDeleteOption,
handleAddColumn: _handleAddColumn, handleDeleteColumn: _handleDeleteColumn,
} = attributeManagement;
// 2025-12-24: 신규 훅 초기화
@@ -185,7 +169,6 @@ export function ItemMasterDataManagement() {
const deleteManagement = useDeleteManagement({ itemPages });
const {
handleDeletePage: handleDeletePageWithTracking,
handleDeleteSection: _handleDeleteSectionWithTracking,
handleUnlinkField: handleUnlinkFieldWithTracking,
handleResetAllData: handleResetAllDataFromHook,
} = deleteManagement;
@@ -268,7 +251,7 @@ export function ItemMasterDataManagement() {
isLoadTemplateDialogOpen, setIsLoadTemplateDialogOpen,
selectedTemplateId, setSelectedTemplateId,
isTemplateFieldDialogOpen, setIsTemplateFieldDialogOpen,
currentTemplateId: _currentTemplateId, setCurrentTemplateId,
setCurrentTemplateId,
editingTemplateFieldId, setEditingTemplateFieldId,
templateFieldName, setTemplateFieldName,
templateFieldKey, setTemplateFieldKey,
@@ -290,14 +273,6 @@ export function ItemMasterDataManagement() {
handleDeleteBOMItemFromTemplate,
} = templateManagement;
// 모든 페이지의 섹션을 하나의 배열로 평탄화
const _itemSections = itemPages.flatMap(page =>
page.sections.map(section => ({
...section,
parentPageId: page.id
}))
);
// 2025-11-26: itemPages의 모든 섹션 + 독립 섹션(independentSections)을 SectionTemplate 형식으로 변환
// 이렇게 하면 계층구조 탭과 섹션 탭이 같은 데이터 소스를 사용하여 자동 동기화됨
// 독립 섹션: 페이지에서 연결 해제된 섹션 (page_id = null)
@@ -481,531 +456,32 @@ export function ItemMasterDataManagement() {
</Button> */}
</div>
{/* 속성 탭 (단위/재질/표면처리 통합) */}
{/* 속성 탭 (단위/재질/표면처리 통합) - 2025-12-24: AttributeTabContent로 분리 */}
<TabsContent value="attributes" className="space-y-4">
<Card>
<CardHeader>
<CardTitle> </CardTitle>
<CardDescription>, , </CardDescription>
</CardHeader>
<CardContent>
{/* 속성 하위 탭 (칩 형태) */}
<div className="flex items-center gap-2 mb-6 border-b pb-2">
<div className="flex gap-2 flex-1 flex-wrap">
{attributeSubTabs.sort((a, b) => a.order - b.order).map(tab => (
<Button
key={tab.id}
variant={activeAttributeTab === tab.key ? 'default' : 'outline'}
size="sm"
onClick={() => setActiveAttributeTab(tab.key)}
className="rounded-full"
>
{tab.label}
</Button>
))}
</div>
<Button
variant="ghost"
size="sm"
onClick={() => setIsManageAttributeTabsDialogOpen(true)}
className="shrink-0"
>
<Settings className="w-4 h-4 mr-1" />
</Button>
</div>
{/* 단위 관리 */}
{activeAttributeTab === 'units' && (
<div className="space-y-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-medium"> </h3>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => {
setManagingColumnType('units');
setNewColumnName('');
setNewColumnKey('');
setNewColumnType('text');
setNewColumnRequired(false);
setIsColumnManageDialogOpen(true);
}}>
<Settings className="w-4 h-4 mr-2" />
</Button>
<Button size="sm" onClick={() => { setEditingOptionType('unit'); setIsOptionDialogOpen(true); }}>
<Plus className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
<div className="space-y-3">
{unitOptions.map((option) => {
const columns = attributeColumns['units'] || [];
const hasColumns = columns.length > 0 && option.columnValues;
return (
<div key={option.id} className="p-4 border rounded hover:bg-gray-50 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium text-base">{option.label}</span>
{option.inputType && (
<Badge variant="outline" className="text-xs">{getInputTypeLabel(option.inputType)}</Badge>
)}
{option.required && (
<Badge variant="destructive" className="text-xs"></Badge>
)}
</div>
<div className="text-sm text-muted-foreground space-y-1">
<div className="flex gap-2">
<span className="font-medium min-w-16">(Value):</span>
<span>{option.value}</span>
</div>
{option.placeholder && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.placeholder}</span>
</div>
)}
{option.defaultValue && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.defaultValue}</span>
</div>
)}
{option.inputType === 'dropdown' && option.options && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<div className="flex flex-wrap gap-1">
{option.options.map((opt, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">{opt}</Badge>
))}
</div>
</div>
)}
</div>
{hasColumns && (
<div className="mt-3 pt-3 border-t">
<p className="text-xs font-medium text-muted-foreground mb-2"> </p>
<div className="grid grid-cols-2 gap-2 text-sm">
{columns.map((column) => (
<div key={column.id} className="flex gap-2">
<span className="text-muted-foreground">{column.name}:</span>
<span>{option.columnValues?.[column.key] || '-'}</span>
</div>
))}
</div>
</div>
)}
</div>
<Button variant="ghost" size="sm" onClick={() => handleDeleteOption('unit', option.id)}>
<Trash2 className="w-4 h-4 text-red-500" />
</Button>
</div>
</div>
);
})}
</div>
</div>
)}
{/* 재질 관리 */}
{activeAttributeTab === 'materials' && (
<div className="space-y-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-medium"> </h3>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => {
setManagingColumnType('materials');
setNewColumnName('');
setNewColumnKey('');
setNewColumnType('text');
setNewColumnRequired(false);
setIsColumnManageDialogOpen(true);
}}>
<Settings className="w-4 h-4 mr-2" />
</Button>
<Button size="sm" onClick={() => { setEditingOptionType('material'); setIsOptionDialogOpen(true); }}>
<Plus className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
<div className="space-y-3">
{materialOptions.map((option) => {
const columns = attributeColumns['materials'] || [];
const hasColumns = columns.length > 0 && option.columnValues;
return (
<div key={option.id} className="p-4 border rounded hover:bg-gray-50 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium text-base">{option.label}</span>
{option.inputType && (
<Badge variant="outline" className="text-xs">{getInputTypeLabel(option.inputType)}</Badge>
)}
{option.required && (
<Badge variant="destructive" className="text-xs"></Badge>
)}
</div>
<div className="text-sm text-muted-foreground space-y-1">
<div className="flex gap-2">
<span className="font-medium min-w-16">(Value):</span>
<span>{option.value}</span>
</div>
{option.placeholder && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.placeholder}</span>
</div>
)}
{option.defaultValue && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.defaultValue}</span>
</div>
)}
{option.inputType === 'dropdown' && option.options && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<div className="flex flex-wrap gap-1">
{option.options.map((opt, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">{opt}</Badge>
))}
</div>
</div>
)}
</div>
{hasColumns && (
<div className="mt-3 pt-3 border-t">
<p className="text-xs font-medium text-muted-foreground mb-2"> </p>
<div className="grid grid-cols-2 gap-2 text-sm">
{columns.map((column) => (
<div key={column.id} className="flex gap-2">
<span className="text-muted-foreground">{column.name}:</span>
<span>{option.columnValues?.[column.key] || '-'}</span>
</div>
))}
</div>
</div>
)}
</div>
<Button variant="ghost" size="sm" onClick={() => handleDeleteOption('material', option.id)}>
<Trash2 className="w-4 h-4 text-red-500" />
</Button>
</div>
</div>
);
})}
</div>
</div>
)}
{/* 표면처리 관리 */}
{activeAttributeTab === 'surface' && (
<div className="space-y-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-medium"> </h3>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => {
setManagingColumnType('surface');
setNewColumnName('');
setNewColumnKey('');
setNewColumnType('text');
setNewColumnRequired(false);
setIsColumnManageDialogOpen(true);
}}>
<Settings className="w-4 h-4 mr-2" />
</Button>
<Button size="sm" onClick={() => { setEditingOptionType('surface'); setIsOptionDialogOpen(true); }}>
<Plus className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
<div className="space-y-3">
{surfaceTreatmentOptions.map((option) => {
const columns = attributeColumns['surface'] || [];
const hasColumns = columns.length > 0 && option.columnValues;
const inputTypeLabel = getInputTypeLabel(option.inputType);
return (
<div key={option.id} className="p-4 border rounded hover:bg-gray-50 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium text-base">{option.label}</span>
{option.inputType && (
<Badge variant="outline" className="text-xs">{inputTypeLabel}</Badge>
)}
{option.required && (
<Badge variant="destructive" className="text-xs"></Badge>
)}
</div>
<div className="text-sm text-muted-foreground space-y-1">
<div className="flex gap-2">
<span className="font-medium min-w-16">(Value):</span>
<span>{option.value}</span>
</div>
{option.placeholder && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.placeholder}</span>
</div>
)}
{option.defaultValue && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.defaultValue}</span>
</div>
)}
{option.inputType === 'dropdown' && option.options && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<div className="flex flex-wrap gap-1">
{option.options.map((opt, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">{opt}</Badge>
))}
</div>
</div>
)}
</div>
{hasColumns && (
<div className="mt-3 pt-3 border-t">
<p className="text-xs font-medium text-muted-foreground mb-2"> </p>
<div className="grid grid-cols-2 gap-2 text-sm">
{columns.map((column) => (
<div key={column.id} className="flex gap-2">
<span className="text-muted-foreground">{column.name}:</span>
<span>{option.columnValues?.[column.key] || '-'}</span>
</div>
))}
</div>
</div>
)}
</div>
<Button variant="ghost" size="sm" onClick={() => handleDeleteOption('surface', option.id)}>
<Trash2 className="w-4 h-4 text-red-500" />
</Button>
</div>
</div>
);
})}
</div>
</div>
)}
{/* 사용자 정의 속성 탭 및 마스터 항목 탭 */}
{!['units', 'materials', 'surface'].includes(activeAttributeTab) && (() => {
const currentTabKey = activeAttributeTab;
// 마스터 항목인지 확인
const masterField = itemMasterFields.find(f => f.id.toString() === currentTabKey);
// 마스터 항목이면 해당 항목의 속성값들을 표시
// Note: properties is Record<string, any> | null, convert to array for display
const propertiesArray = masterField?.properties
? Object.entries(masterField.properties).map(([key, value]) => ({ key, ...value as object }))
: [];
if (masterField && propertiesArray.length > 0) {
return (
<div className="space-y-4">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="font-medium">{masterField.field_name} </h3>
<p className="text-sm text-muted-foreground mt-1">
"{masterField.field_name}"
</p>
</div>
</div>
<div className="space-y-3">
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
{propertiesArray.map((property: any) => {
const inputTypeLabel = getInputTypeLabel(property.type);
return (
<div key={property.id} className="p-4 border rounded hover:bg-gray-50 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium text-base">{property.label}</span>
<Badge variant="outline" className="text-xs">{inputTypeLabel}</Badge>
{property.required && (
<Badge variant="destructive" className="text-xs"></Badge>
)}
</div>
<div className="text-sm text-muted-foreground space-y-1">
<div className="flex gap-2">
<span className="font-medium min-w-24">(Key):</span>
<code className="bg-gray-100 px-2 py-0.5 rounded text-xs">{property.key}</code>
</div>
{property.placeholder && (
<div className="flex gap-2">
<span className="font-medium min-w-24">:</span>
<span>{property.placeholder}</span>
</div>
)}
{property.defaultValue && (
<div className="flex gap-2">
<span className="font-medium min-w-24">:</span>
<span>{property.defaultValue}</span>
</div>
)}
{property.type === 'dropdown' && property.options && (
<div className="flex gap-2">
<span className="font-medium min-w-24">:</span>
<div className="flex flex-wrap gap-1">
{property.options.map((opt: string, idx: number) => (
<Badge key={idx} variant="secondary" className="text-xs">{opt}</Badge>
))}
</div>
</div>
)}
</div>
</div>
</div>
</div>
);
})}
</div>
<div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<div className="flex items-start gap-2">
<Package className="w-5 h-5 text-blue-600 mt-0.5" />
<div className="flex-1">
<p className="text-sm font-medium text-blue-900">
</p>
<p className="text-xs text-blue-700 mt-1">
<strong> </strong> "{masterField.field_name}" // .
</p>
</div>
</div>
</div>
</div>
);
}
// 사용자 정의 속성 탭 (기존 로직)
const currentOptions = customAttributeOptions[currentTabKey] || [];
return (
<div className="space-y-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-medium">
{attributeSubTabs.find(t => t.key === activeAttributeTab)?.label || '사용자 정의'}
</h3>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => {
setManagingColumnType(currentTabKey);
setNewColumnName('');
setNewColumnKey('');
setNewColumnType('text');
setNewColumnRequired(false);
setIsColumnManageDialogOpen(true);
}}>
<Settings className="w-4 h-4 mr-2" />
</Button>
<Button size="sm" onClick={() => {
setEditingOptionType(activeAttributeTab);
setNewOptionValue('');
setNewOptionLabel('');
setNewOptionColumnValues({});
setIsOptionDialogOpen(true);
}}>
<Plus className="w-4 h-4 mr-2" />
</Button>
</div>
</div>
{currentOptions.length > 0 ? (
<div className="space-y-3">
{currentOptions.map((option) => {
const columns = attributeColumns[currentTabKey] || [];
const hasColumns = columns.length > 0 && option.columnValues;
const inputTypeLabel = getInputTypeLabel(option.inputType);
return (
<div key={option.id} className="p-4 border rounded hover:bg-gray-50 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium text-base">{option.label}</span>
<Badge variant="outline" className="text-xs">{inputTypeLabel}</Badge>
{option.required && (
<Badge variant="destructive" className="text-xs"></Badge>
)}
</div>
<div className="text-sm text-muted-foreground space-y-1">
<div className="flex gap-2">
<span className="font-medium min-w-16">(Value):</span>
<span>{option.value}</span>
</div>
{option.placeholder && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.placeholder}</span>
</div>
)}
{option.defaultValue && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<span>{option.defaultValue}</span>
</div>
)}
{option.inputType === 'dropdown' && option.options && (
<div className="flex gap-2">
<span className="font-medium min-w-16">:</span>
<div className="flex flex-wrap gap-1">
{option.options.map((opt, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">{opt}</Badge>
))}
</div>
</div>
)}
</div>
{hasColumns && (
<div className="mt-3 pt-3 border-t">
<p className="text-xs font-medium text-muted-foreground mb-2"> </p>
<div className="grid grid-cols-2 gap-2 text-sm">
{columns.map((column) => (
<div key={column.id} className="flex gap-2">
<span className="text-muted-foreground">{column.name}:</span>
<span>{option.columnValues?.[column.key] || '-'}</span>
</div>
))}
</div>
</div>
)}
</div>
<Button variant="ghost" size="sm" onClick={() => handleDeleteOption(currentTabKey, option.id)}>
<Trash2 className="w-4 h-4 text-red-500" />
</Button>
</div>
</div>
);
})}
</div>
) : (
<div className="border-2 border-dashed rounded-lg p-8 text-center text-gray-500">
<Settings className="w-12 h-12 mx-auto mb-4 text-gray-400" />
<p className="mb-2"> </p>
<p className="text-sm"> "추가" </p>
</div>
)}
</div>
);
})()}
</CardContent>
</Card>
<AttributeTabContent
activeAttributeTab={activeAttributeTab}
setActiveAttributeTab={setActiveAttributeTab}
attributeSubTabs={attributeSubTabs}
unitOptions={unitOptions}
materialOptions={materialOptions}
surfaceTreatmentOptions={surfaceTreatmentOptions}
customAttributeOptions={customAttributeOptions}
attributeColumns={attributeColumns}
itemMasterFields={itemMasterFields}
setIsManageAttributeTabsDialogOpen={setIsManageAttributeTabsDialogOpen}
setIsOptionDialogOpen={setIsOptionDialogOpen}
setEditingOptionType={setEditingOptionType}
setNewOptionValue={setNewOptionValue}
setNewOptionLabel={setNewOptionLabel}
setNewOptionColumnValues={setNewOptionColumnValues}
setIsColumnManageDialogOpen={setIsColumnManageDialogOpen}
setManagingColumnType={setManagingColumnType}
setNewColumnName={setNewColumnName}
setNewColumnKey={setNewColumnKey}
setNewColumnType={setNewColumnType}
setNewColumnRequired={setNewColumnRequired}
handleDeleteOption={handleDeleteOption}
/>
</TabsContent>
{/* 항목 탭 */}