Files
sam-react-prod/src/components/items/ItemMasterDataManagement.tsx

1561 lines
75 KiB
TypeScript
Raw Normal View History

'use client';
import { useState, useEffect } from 'react';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
import { useItemMaster } from '@/contexts/ItemMasterContext';
import type { SectionTemplate, BOMItem } from '@/contexts/ItemMasterContext';
import { MasterFieldTab, HierarchyTab, SectionsTab } from './ItemMasterDataManagement/tabs';
import { FieldDialog } from './ItemMasterDataManagement/dialogs/FieldDialog';
// ConditionalFieldConfig type removed - not currently used
import { FieldDrawer } from './ItemMasterDataManagement/dialogs/FieldDrawer';
import { TabManagementDialogs } from './ItemMasterDataManagement/dialogs/TabManagementDialogs';
import { OptionDialog } from './ItemMasterDataManagement/dialogs/OptionDialog';
import { ColumnManageDialog } from './ItemMasterDataManagement/dialogs/ColumnManageDialog';
import { PathEditDialog } from './ItemMasterDataManagement/dialogs/PathEditDialog';
import { PageDialog } from './ItemMasterDataManagement/dialogs/PageDialog';
import { SectionDialog } from './ItemMasterDataManagement/dialogs/SectionDialog';
import { MasterFieldDialog } from './ItemMasterDataManagement/dialogs/MasterFieldDialog';
import { TemplateFieldDialog } from './ItemMasterDataManagement/dialogs/TemplateFieldDialog';
import { LoadTemplateDialog } from './ItemMasterDataManagement/dialogs/LoadTemplateDialog';
import { ColumnDialog } from './ItemMasterDataManagement/dialogs/ColumnDialog';
import { SectionTemplateDialog } from './ItemMasterDataManagement/dialogs/SectionTemplateDialog';
import { itemMasterApi } from '@/lib/api/item-master';
import { getErrorMessage, ApiError } from '@/lib/api/error-handler';
import { LoadingSpinner } from '@/components/ui/loading-spinner';
import { ErrorMessage } from '@/components/ui/error-message';
import {
transformPagesResponse,
transformSectionTemplatesResponse,
transformMasterFieldsResponse,
transformCustomTabsResponse,
transformUnitOptionsResponse,
} from '@/lib/api/transformers';
import {
Database,
Plus,
Trash2,
FileText,
Settings,
Package,
} from 'lucide-react';
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 {
usePageManagement,
useSectionManagement,
useFieldManagement,
useMasterFieldManagement,
useTemplateManagement,
useAttributeManagement,
useTabManagement,
} from './ItemMasterDataManagement/hooks';
const ITEM_TYPE_OPTIONS = [
{ value: 'FG', label: '제품 (FG)' },
{ value: 'PT', label: '부품 (PT)' },
{ value: 'SM', label: '부자재 (SM)' },
{ value: 'RM', label: '원자재 (RM)' },
{ value: 'CS', label: '소모품 (CS)' }
];
const INPUT_TYPE_OPTIONS = [
{ value: 'textbox', label: '텍스트박스' },
{ value: 'dropdown', label: '드롭다운' },
{ value: 'checkbox', label: '체크박스' },
{ value: 'number', label: '숫자' },
{ value: 'date', label: '날짜' },
{ value: 'textarea', label: '텍스트영역' }
];
export function ItemMasterDataManagement() {
const {
itemPages,
loadItemPages,
addItemPage: _addItemPage,
updateItemPage,
deleteItemPage,
addSectionToPage: _addSectionToPage,
updateSection,
deleteSection,
addFieldToSection: _addFieldToSection,
updateField: _updateField,
deleteField,
reorderFields,
itemMasterFields,
loadItemMasterFields,
addItemMasterField: _addItemMasterField,
updateItemMasterField: _updateItemMasterField,
deleteItemMasterField: _deleteItemMasterField,
sectionTemplates,
loadSectionTemplates,
addSectionTemplate: _addSectionTemplate,
updateSectionTemplate: _updateSectionTemplate,
deleteSectionTemplate: _deleteSectionTemplate,
resetAllData,
tenantId: _tenantId
} = useItemMaster();
console.log('ItemMasterDataManagement: Current sectionTemplates', sectionTemplates);
// ===== 커스텀 훅 초기화 =====
const pageManagement = usePageManagement();
const sectionManagement = useSectionManagement();
const fieldManagement = useFieldManagement();
const masterFieldManagement = useMasterFieldManagement();
const templateManagement = useTemplateManagement();
const attributeManagement = useAttributeManagement();
const tabManagement = useTabManagement();
// 훅에서 필요한 값들 구조분해
const {
selectedPageId, setSelectedPageId, selectedPage,
editingPageId, setEditingPageId, editingPageName, setEditingPageName,
isPageDialogOpen, setIsPageDialogOpen,
newPageName, setNewPageName, newPageItemType, setNewPageItemType,
editingPathPageId, setEditingPathPageId, editingAbsolutePath, setEditingAbsolutePath,
handleAddPage, handleDuplicatePage, handleDeletePage: _handleDeletePage,
handleUpdatePageName: _handleUpdatePageName, handleUpdateAbsolutePath: _handleUpdateAbsolutePath,
} = pageManagement;
const {
editingSectionId, setEditingSectionId,
editingSectionTitle, setEditingSectionTitle,
isSectionDialogOpen, setIsSectionDialogOpen,
newSectionTitle, setNewSectionTitle,
newSectionDescription, setNewSectionDescription,
newSectionType, setNewSectionType,
sectionInputMode, setSectionInputMode,
selectedSectionTemplateId, setSelectedSectionTemplateId,
expandedSections: _expandedSections, setExpandedSections: _setExpandedSections,
handleAddSection, handleLinkTemplate,
handleEditSectionTitle, handleSaveSectionTitle,
handleDeleteSection: _handleDeleteSection, toggleSection: _toggleSection,
} = sectionManagement;
const {
isFieldDialogOpen, setIsFieldDialogOpen,
selectedSectionForField, setSelectedSectionForField,
editingFieldId, setEditingFieldId,
fieldInputMode, setFieldInputMode,
showMasterFieldList, setShowMasterFieldList,
selectedMasterFieldId, setSelectedMasterFieldId,
newFieldName, setNewFieldName,
newFieldKey, setNewFieldKey,
newFieldInputType, setNewFieldInputType,
newFieldRequired, setNewFieldRequired,
newFieldOptions, setNewFieldOptions,
newFieldDescription, setNewFieldDescription,
textboxColumns, setTextboxColumns,
isColumnDialogOpen, setIsColumnDialogOpen,
editingColumnId, setEditingColumnId,
columnName, setColumnName,
columnKey, setColumnKey,
newFieldConditionEnabled, setNewFieldConditionEnabled,
newFieldConditionTargetType, setNewFieldConditionTargetType,
newFieldConditionFields, setNewFieldConditionFields,
newFieldConditionSections, setNewFieldConditionSections,
tempConditionValue, setTempConditionValue,
handleAddField, handleEditField, handleDeleteField: _handleDeleteField,
} = fieldManagement;
const {
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,
handleAddMasterField, handleEditMasterField,
handleUpdateMasterField, handleDeleteMasterField,
} = masterFieldManagement;
const {
isSectionTemplateDialogOpen, setIsSectionTemplateDialogOpen,
editingSectionTemplateId, setEditingSectionTemplateId,
newSectionTemplateTitle, setNewSectionTemplateTitle,
newSectionTemplateDescription, setNewSectionTemplateDescription,
newSectionTemplateCategory, setNewSectionTemplateCategory,
newSectionTemplateType, setNewSectionTemplateType,
isLoadTemplateDialogOpen, setIsLoadTemplateDialogOpen,
selectedTemplateId, setSelectedTemplateId,
isTemplateFieldDialogOpen, setIsTemplateFieldDialogOpen,
currentTemplateId: _currentTemplateId, setCurrentTemplateId,
editingTemplateFieldId, setEditingTemplateFieldId,
templateFieldName, setTemplateFieldName,
templateFieldKey, setTemplateFieldKey,
templateFieldInputType, setTemplateFieldInputType,
templateFieldRequired, setTemplateFieldRequired,
templateFieldOptions, setTemplateFieldOptions,
templateFieldDescription, setTemplateFieldDescription,
templateFieldMultiColumn, setTemplateFieldMultiColumn,
templateFieldColumnCount, setTemplateFieldColumnCount,
templateFieldColumnNames, setTemplateFieldColumnNames,
templateFieldInputMode, setTemplateFieldInputMode,
templateFieldShowMasterFieldList, setTemplateFieldShowMasterFieldList,
templateFieldSelectedMasterFieldId, setTemplateFieldSelectedMasterFieldId,
handleAddSectionTemplate, handleEditSectionTemplate,
handleUpdateSectionTemplate, handleDeleteSectionTemplate,
handleLoadTemplate, handleAddTemplateField,
handleEditTemplateField, handleDeleteTemplateField,
handleAddBOMItemToTemplate, handleUpdateBOMItemInTemplate,
handleDeleteBOMItemFromTemplate,
} = templateManagement;
const {
unitOptions, setUnitOptions,
materialOptions, setMaterialOptions,
surfaceTreatmentOptions, setSurfaceTreatmentOptions,
customAttributeOptions, setCustomAttributeOptions,
isOptionDialogOpen, setIsOptionDialogOpen,
editingOptionType, setEditingOptionType,
newOptionValue, setNewOptionValue,
newOptionLabel, setNewOptionLabel,
newOptionColumnValues, setNewOptionColumnValues,
newOptionInputType, setNewOptionInputType,
newOptionRequired, setNewOptionRequired,
newOptionOptions, setNewOptionOptions,
newOptionPlaceholder, setNewOptionPlaceholder,
newOptionDefaultValue, setNewOptionDefaultValue,
isColumnManageDialogOpen, setIsColumnManageDialogOpen,
managingColumnType, setManagingColumnType,
attributeColumns, setAttributeColumns,
newColumnName, setNewColumnName,
newColumnKey, setNewColumnKey,
newColumnType, setNewColumnType,
newColumnRequired, setNewColumnRequired,
handleAddOption, handleDeleteOption,
handleAddColumn: _handleAddColumn, handleDeleteColumn: _handleDeleteColumn,
} = attributeManagement;
const {
customTabs, setCustomTabs,
activeTab, setActiveTab,
attributeSubTabs, setAttributeSubTabs,
activeAttributeTab, setActiveAttributeTab,
isAddTabDialogOpen, setIsAddTabDialogOpen,
isManageTabsDialogOpen, setIsManageTabsDialogOpen,
newTabLabel, setNewTabLabel,
editingTabId, setEditingTabId,
deletingTabId, setDeletingTabId,
isDeleteTabDialogOpen, setIsDeleteTabDialogOpen,
isManageAttributeTabsDialogOpen, setIsManageAttributeTabsDialogOpen,
isAddAttributeTabDialogOpen, setIsAddAttributeTabDialogOpen,
newAttributeTabLabel, setNewAttributeTabLabel,
editingAttributeTabId, setEditingAttributeTabId,
deletingAttributeTabId, setDeletingAttributeTabId,
isDeleteAttributeTabDialogOpen, setIsDeleteAttributeTabDialogOpen,
handleAddTab, handleUpdateTab, handleDeleteTab, confirmDeleteTab,
handleAddAttributeTab, handleUpdateAttributeTab,
handleDeleteAttributeTab, confirmDeleteAttributeTab,
moveTabUp, moveTabDown,
moveAttributeTabUp, moveAttributeTabDown,
getTabIcon, handleEditTabFromManage,
} = tabManagement;
// 모든 페이지의 섹션을 하나의 배열로 평탄화
const _itemSections = itemPages.flatMap(page =>
page.sections.map(section => ({
...section,
parentPageId: page.id
}))
);
// 마운트 상태 추적 (SSR 호환)
const [_mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// API 로딩 및 에러 상태 관리
const [isInitialLoading, setIsInitialLoading] = useState(true); // 초기 데이터 로딩
const [_isLoading, _setIsLoading] = useState(false); // 개별 작업 로딩
const [error, setError] = useState<string | null>(null); // 에러 메시지
// 초기 데이터 로딩
useEffect(() => {
const loadInitialData = async () => {
try {
setIsInitialLoading(true);
setError(null);
const data = await itemMasterApi.init();
// 페이지 데이터 로드 (이미 존재하는 데이터를 state에 로드 - API 호출 없음)
const transformedPages = transformPagesResponse(data.pages);
loadItemPages(transformedPages);
// 섹션 템플릿 로드 (덮어쓰기 - API 호출 없음!)
const transformedTemplates = transformSectionTemplatesResponse(data.sectionTemplates);
loadSectionTemplates(transformedTemplates);
// 마스터 필드 로드 (덮어쓰기 - API 호출 없음!)
const transformedFields = transformMasterFieldsResponse(data.masterFields);
loadItemMasterFields(transformedFields);
// 커스텀 탭 로드 (local state)
if (data.customTabs && data.customTabs.length > 0) {
const transformedTabs = transformCustomTabsResponse(data.customTabs);
setCustomTabs(prev => [...prev, ...transformedTabs]);
}
// 단위 옵션 로드 (local state)
if (data.unitOptions && data.unitOptions.length > 0) {
const transformedUnits = transformUnitOptionsResponse(data.unitOptions);
setUnitOptions(transformedUnits);
}
console.log('✅ Initial data loaded:', {
pages: data.pages.length,
templates: data.sectionTemplates.length,
masterFields: data.masterFields.length,
customTabs: data.customTabs?.length || 0,
unitOptions: data.unitOptions?.length || 0,
});
} catch (err) {
if (err instanceof ApiError && err.errors) {
// Validation 에러 (422)
const errorMessages = Object.entries(err.errors)
.map(([field, messages]) => `${field}: ${messages.join(', ')}`)
.join('\n');
toast.error(errorMessages);
setError('입력값을 확인해주세요.');
} else {
const errorMessage = getErrorMessage(err);
setError(errorMessage);
toast.error(errorMessage);
}
console.error('❌ Failed to load initial data:', err);
} finally {
setIsInitialLoading(false);
}
};
loadInitialData();
}, []);
// ===== 훅으로 이동된 상태들 (참조용 주석) =====
// 탭, 속성, 페이지, 섹션 관련 상태는 위의 훅에서 관리됩니다.
// 모바일 체크
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768);
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
// 필드, 마스터필드, 템플릿 관련 상태는 위의 훅에서 관리됩니다.
// BOM 관리 상태 (훅에 없음)
const [_bomItems, setBomItems] = useState<BOMItem[]>([]);
// ===== 이하 핸들러들은 훅으로 이동되어 제거됨 =====
// handleAddOption, handleDeleteOption → useAttributeManagement
// handleAddPage, handleDuplicatePage → usePageManagement
// handleAddSection, handleLinkTemplate, handleEditSectionTitle, handleSaveSectionTitle → useSectionManagement
// handleAddField, handleEditField → useFieldManagement
// handleAddMasterField, handleEditMasterField, handleUpdateMasterField, handleDeleteMasterField → useMasterFieldManagement
// handleAddSectionTemplate 등 템플릿 관련 → useTemplateManagement
// handleAddTab 등 탭 관련 → useTabManagement
// 페이지 삭제 핸들러 (pendingChanges 제거 포함) - 훅에 없어 유지
const handleDeletePageWithTracking = (pageId: number) => {
const pageToDelete = itemPages.find(p => p.id === pageId);
const sectionIds = pageToDelete?.sections.map(s => s.id) || [];
const fieldIds = pageToDelete?.sections.flatMap(s => s.fields?.map(f => f.id) || []) || [];
deleteItemPage(pageId);
console.log('페이지 삭제 완료:', { pageId, removedSections: sectionIds.length, removedFields: fieldIds.length });
};
// 섹션 삭제 핸들러 (pendingChanges 제거 포함) - 훅에 없어 유지
const handleDeleteSectionWithTracking = (pageId: number, sectionId: number) => {
const page = itemPages.find(p => p.id === pageId);
const sectionToDelete = page?.sections.find(s => s.id === sectionId);
const fieldIds = sectionToDelete?.fields?.map(f => f.id) || [];
deleteSection(Number(sectionId));
console.log('섹션 삭제 완료:', { sectionId, removedFields: fieldIds.length });
};
// 필드 삭제 핸들러 (pendingChanges 제거 포함) - 훅에 없어 유지
const handleDeleteFieldWithTracking = (_pageId: string, _sectionId: string, fieldId: string) => {
deleteField(Number(fieldId));
console.log('필드 삭제 완료:', fieldId);
};
// 절대경로 업데이트 - 로컬에서 처리
const _handleUpdateAbsolutePathLocal = (pageId: number, newPath: string) => {
updateItemPage(pageId, { absolute_path: newPath });
toast.success('절대경로가 업데이트되었습니다');
};
// 필드 순서 변경
const _handleReorderFieldsLocal = (sectionId: number, orderedFieldIds: number[]) => {
reorderFields(sectionId, orderedFieldIds);
};
// 페이지 이름 업데이트
const _handleUpdatePageNameLocal = (pageId: number, newName: string) => {
updateItemPage(pageId, { page_name: newName });
toast.success('페이지 이름이 업데이트되었습니다');
};
// ===== 래퍼 함수들 (훅 함수에 selectedPage 바인딩 및 타입 호환성) =====
const handleAddSectionWrapper = () => handleAddSection(selectedPage);
const handleLinkTemplateWrapper = (template: SectionTemplate) => handleLinkTemplate(template, selectedPage);
const handleSaveSectionTitleWrapper = () => handleSaveSectionTitle(selectedPage);
const handleAddFieldWrapper = () => handleAddField(selectedPage);
const handleLoadTemplateWrapper = () => handleLoadTemplate(selectedPage);
// setter 래퍼들 (Dispatch<SetStateAction> 타입 호환성)
const setNewSectionTypeWrapper: React.Dispatch<React.SetStateAction<'fields' | 'bom'>> = setNewSectionType as any;
const setNewPageItemTypeWrapper: React.Dispatch<React.SetStateAction<'FG' | 'PT' | 'SM' | 'RM' | 'CS'>> = setNewPageItemType as any;
// ===== 유틸리티 함수들 =====
// 현재 섹션의 모든 필드 가져오기 (조건부 필드 참조용)
const _getAllFieldsInSection = (sectionId: number) => {
if (!selectedPage) return [];
const section = selectedPage.sections.find(s => s.id === sectionId);
return section?.fields || [];
};
// 섹션 순서 변경 핸들러 (드래그앤드롭)
const moveSection = (dragIndex: number, hoverIndex: number) => {
if (!selectedPage) return;
const sections = [...selectedPage.sections];
const [draggedSection] = sections.splice(dragIndex, 1);
sections.splice(hoverIndex, 0, draggedSection);
// order 값 재설정
const updatedSections = sections.map((section, idx) => ({
...section,
order: idx + 1
}));
// 페이지 업데이트
updateItemPage(selectedPage.id, { sections: updatedSections });
// hasUnsavedChanges는 computed value이므로 자동 계산됨
toast.success('섹션 순서가 변경되었습니다 (저장 필요)');
};
// 필드 순서 변경 핸들러
const moveField = (sectionId: number, dragIndex: number, hoverIndex: number) => {
if (!selectedPage) return;
const section = selectedPage.sections.find(s => s.id === sectionId);
if (!section || !section.fields) return;
const newFields = [...section.fields];
const [draggedField] = newFields.splice(dragIndex, 1);
newFields.splice(hoverIndex, 0, draggedField);
reorderFields(sectionId, newFields.map(f => f.id));
// hasUnsavedChanges는 computed value이므로 자동 계산됨
};
// 전체 데이터 초기화 핸들러
const _handleResetAllData = () => {
if (!confirm('⚠️ 경고: 모든 품목기준관리 데이터(계층구조, 섹션, 항목, 속성)를 초기화하시겠습니까?\n\n이 작업은 되돌릴 수 없습니다!')) {
return;
}
try {
// ItemMasterContext의 모든 데이터 및 캐시 초기화
resetAllData();
// 로컬 상태 초기화 (ItemMasterContext가 관리하지 않는 컴포넌트 로컬 상태)
setUnitOptions([]);
setMaterialOptions([]);
setSurfaceTreatmentOptions([]);
setCustomAttributeOptions({});
setAttributeColumns({});
setBomItems([]);
// 탭 상태 초기화 (기본 탭만 남김)
setCustomTabs([
{ id: 'hierarchy', label: '계층구조', icon: 'FolderTree', isDefault: true, order: 1 },
{ id: 'sections', label: '섹션', icon: 'Layers', isDefault: true, order: 2 },
{ id: 'items', label: '항목', icon: 'ListTree', isDefault: true, order: 3 },
{ id: 'attributes', label: '속성', icon: 'Settings', isDefault: true, order: 4 }
]);
setAttributeSubTabs([]);
console.log('🗑️ 모든 품목기준관리 데이터가 초기화되었습니다');
toast.success('✅ 모든 데이터가 초기화되었습니다!\n계층구조, 섹션, 항목, 속성이 모두 삭제되었습니다.');
// 페이지 새로고침하여 완전히 초기화된 상태 반영
setTimeout(() => {
window.location.reload();
}, 1500);
} catch (error) {
toast.error('초기화 중 오류가 발생했습니다');
console.error('Reset error:', error);
}
};
// 초기 로딩 중 UI
if (isInitialLoading) {
return (
<div className="flex items-center justify-center min-h-screen">
<LoadingSpinner size="lg" text="데이터를 불러오는 중..." />
</div>
);
}
// 에러 발생 시 UI
if (error) {
return (
<div className="flex items-center justify-center min-h-screen p-4">
<ErrorMessage
message={error}
onRetry={() => window.location.reload()}
/>
</div>
);
}
return (
<PageLayout>
<PageHeader
title="품목기준관리"
description="품목관리에서 사용되는 기준 정보를 설정하고 관리합니다"
icon={Database}
/>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<div className="flex items-center gap-2 mb-4">
<TabsList className="flex-1">
{customTabs.sort((a, b) => a.order - b.order).map(tab => {
const Icon = getTabIcon(tab.icon);
return (
<TabsTrigger key={tab.id} value={tab.id}>
<Icon className="w-4 h-4 mr-2" />
{tab.label}
</TabsTrigger>
);
})}
</TabsList>
{/*<Button*/}
{/* size="sm"*/}
{/* variant="outline"*/}
{/* onClick={() => setIsManageTabsDialogOpen(true)}*/}
{/*>*/}
{/* <Settings className="h-4 w-4 mr-1" />*/}
{/* 탭 관리*/}
{/*</Button>*/}
{/* 전체 초기화 버튼 숨김 처리 - 디자인에 없는 기능 */}
{/* <Button
size="sm"
variant="destructive"
onClick={handleResetAllData}
>
<Trash2 className="h-4 w-4 mr-1" />
</Button> */}
</div>
{/* 속성 탭 (단위/재질/표면처리 통합) */}
<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;
const inputTypeLabel =
option.inputType === 'textbox' ? '텍스트박스' :
option.inputType === 'number' ? '숫자' :
option.inputType === 'dropdown' ? '드롭다운' :
option.inputType === 'checkbox' ? '체크박스' :
option.inputType === 'date' ? '날짜' :
option.inputType === 'textarea' ? '텍스트영역' :
'텍스트박스';
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('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;
const inputTypeLabel =
option.inputType === 'textbox' ? '텍스트박스' :
option.inputType === 'number' ? '숫자' :
option.inputType === 'dropdown' ? '드롭다운' :
option.inputType === 'checkbox' ? '체크박스' :
option.inputType === 'date' ? '날짜' :
option.inputType === 'textarea' ? '텍스트영역' :
'텍스트박스';
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('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 =
option.inputType === 'textbox' ? '텍스트박스' :
option.inputType === 'number' ? '숫자' :
option.inputType === 'dropdown' ? '드롭다운' :
option.inputType === 'checkbox' ? '체크박스' :
option.inputType === 'date' ? '날짜' :
option.inputType === 'textarea' ? '텍스트영역' :
'텍스트박스';
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">
{propertiesArray.map((property: any) => {
const inputTypeLabel =
property.type === 'textbox' ? '텍스트박스' :
property.type === 'number' ? '숫자' :
property.type === 'dropdown' ? '드롭다운' :
property.type === 'checkbox' ? '체크박스' :
property.type === 'date' ? '날짜' :
property.type === 'textarea' ? '텍스트영역' :
'텍스트박스';
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 =
option.inputType === 'textbox' ? '텍스트박스' :
option.inputType === 'number' ? '숫자' :
option.inputType === 'dropdown' ? '드롭다운' :
option.inputType === 'checkbox' ? '체크박스' :
option.inputType === 'date' ? '날짜' :
option.inputType === 'textarea' ? '텍스트영역' :
'텍스트박스';
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>
</TabsContent>
{/* 항목 탭 */}
<TabsContent value="items" className="space-y-4">
<MasterFieldTab
itemMasterFields={itemMasterFields}
setIsMasterFieldDialogOpen={setIsMasterFieldDialogOpen}
handleEditMasterField={handleEditMasterField}
handleDeleteMasterField={handleDeleteMasterField}
hasUnsavedChanges={false}
pendingChanges={{ masterFields: [] }}
/>
</TabsContent>
{/* 섹션관리 탭 */}
<TabsContent value="sections" className="space-y-4">
<SectionsTab
sectionTemplates={sectionTemplates}
setIsSectionTemplateDialogOpen={setIsSectionTemplateDialogOpen}
setCurrentTemplateId={setCurrentTemplateId}
setIsTemplateFieldDialogOpen={setIsTemplateFieldDialogOpen}
handleEditSectionTemplate={handleEditSectionTemplate}
handleDeleteSectionTemplate={handleDeleteSectionTemplate}
handleEditTemplateField={handleEditTemplateField}
handleDeleteTemplateField={handleDeleteTemplateField}
handleAddBOMItemToTemplate={handleAddBOMItemToTemplate}
handleUpdateBOMItemInTemplate={handleUpdateBOMItemInTemplate}
handleDeleteBOMItemFromTemplate={handleDeleteBOMItemFromTemplate}
ITEM_TYPE_OPTIONS={ITEM_TYPE_OPTIONS}
INPUT_TYPE_OPTIONS={INPUT_TYPE_OPTIONS}
/>
</TabsContent>
{/* 계층구조 탭 */}
<TabsContent value="hierarchy" className="space-y-4">
<HierarchyTab
itemPages={itemPages}
selectedPage={selectedPage}
ITEM_TYPE_OPTIONS={ITEM_TYPE_OPTIONS}
editingPageId={editingPageId}
setEditingPageId={setEditingPageId}
editingPageName={editingPageName}
setEditingPageName={setEditingPageName}
selectedPageId={selectedPageId}
setSelectedPageId={setSelectedPageId}
editingPathPageId={editingPathPageId}
setEditingPathPageId={setEditingPathPageId}
editingAbsolutePath={editingAbsolutePath}
setEditingAbsolutePath={setEditingAbsolutePath}
editingSectionId={editingSectionId}
setEditingSectionId={setEditingSectionId}
editingSectionTitle={editingSectionTitle}
setEditingSectionTitle={setEditingSectionTitle}
hasUnsavedChanges={false}
pendingChanges={{ pages: [], sections: [], fields: [], masterFields: [], attributes: [], sectionTemplates: [] }}
selectedSectionForField={selectedSectionForField}
setSelectedSectionForField={setSelectedSectionForField}
newSectionType={newSectionType}
setNewSectionType={setNewSectionTypeWrapper}
updateItemPage={updateItemPage}
trackChange={() => {}}
deleteItemPage={handleDeletePageWithTracking}
duplicatePage={handleDuplicatePage}
setIsPageDialogOpen={setIsPageDialogOpen}
setIsSectionDialogOpen={setIsSectionDialogOpen}
setIsFieldDialogOpen={setIsFieldDialogOpen}
handleEditSectionTitle={handleEditSectionTitle}
handleSaveSectionTitle={handleSaveSectionTitleWrapper}
moveSection={moveSection}
deleteSection={handleDeleteSectionWithTracking}
updateSection={updateSection}
deleteField={handleDeleteFieldWithTracking}
handleEditField={handleEditField}
moveField={moveField}
/>
</TabsContent>
{/* 사용자 정의 탭들 */}
{customTabs.filter(tab => !tab.isDefault).map(tab => (
<TabsContent key={tab.id} value={tab.id}>
<Card>
<CardHeader>
<CardTitle>{tab.label}</CardTitle>
<CardDescription> . .</CardDescription>
</CardHeader>
<CardContent>
<div className="text-center py-12">
<FileText className="w-16 h-16 mx-auto text-gray-300 mb-4" />
<p className="text-muted-foreground mb-2">{tab.label} </p>
<p className="text-sm text-muted-foreground">
</p>
</div>
</CardContent>
</Card>
</TabsContent>
))}
</Tabs>
<TabManagementDialogs
isManageTabsDialogOpen={isManageTabsDialogOpen}
setIsManageTabsDialogOpen={setIsManageTabsDialogOpen}
customTabs={customTabs}
moveTabUp={moveTabUp}
moveTabDown={moveTabDown}
handleEditTabFromManage={handleEditTabFromManage}
handleDeleteTab={handleDeleteTab}
getTabIcon={getTabIcon}
setIsAddTabDialogOpen={setIsAddTabDialogOpen}
isDeleteTabDialogOpen={isDeleteTabDialogOpen}
setIsDeleteTabDialogOpen={setIsDeleteTabDialogOpen}
deletingTabId={deletingTabId}
setDeletingTabId={setDeletingTabId}
confirmDeleteTab={confirmDeleteTab}
isAddTabDialogOpen={isAddTabDialogOpen}
editingTabId={editingTabId}
setEditingTabId={setEditingTabId}
newTabLabel={newTabLabel}
setNewTabLabel={setNewTabLabel}
handleUpdateTab={handleUpdateTab}
handleAddTab={handleAddTab}
isManageAttributeTabsDialogOpen={isManageAttributeTabsDialogOpen}
setIsManageAttributeTabsDialogOpen={setIsManageAttributeTabsDialogOpen}
attributeSubTabs={attributeSubTabs}
moveAttributeTabUp={moveAttributeTabUp}
moveAttributeTabDown={moveAttributeTabDown}
handleDeleteAttributeTab={handleDeleteAttributeTab}
isDeleteAttributeTabDialogOpen={isDeleteAttributeTabDialogOpen}
setIsDeleteAttributeTabDialogOpen={setIsDeleteAttributeTabDialogOpen}
deletingAttributeTabId={deletingAttributeTabId}
setDeletingAttributeTabId={setDeletingAttributeTabId}
confirmDeleteAttributeTab={confirmDeleteAttributeTab}
isAddAttributeTabDialogOpen={isAddAttributeTabDialogOpen}
setIsAddAttributeTabDialogOpen={setIsAddAttributeTabDialogOpen}
editingAttributeTabId={editingAttributeTabId}
setEditingAttributeTabId={setEditingAttributeTabId}
newAttributeTabLabel={newAttributeTabLabel}
setNewAttributeTabLabel={setNewAttributeTabLabel}
handleUpdateAttributeTab={handleUpdateAttributeTab}
handleAddAttributeTab={handleAddAttributeTab}
/>
<OptionDialog
isOpen={isOptionDialogOpen}
setIsOpen={setIsOptionDialogOpen}
newOptionValue={newOptionValue}
setNewOptionValue={setNewOptionValue}
newOptionLabel={newOptionLabel}
setNewOptionLabel={setNewOptionLabel}
newOptionColumnValues={newOptionColumnValues}
setNewOptionColumnValues={setNewOptionColumnValues}
newOptionInputType={newOptionInputType}
setNewOptionInputType={setNewOptionInputType}
newOptionRequired={newOptionRequired}
setNewOptionRequired={setNewOptionRequired}
newOptionOptions={newOptionOptions}
setNewOptionOptions={setNewOptionOptions}
newOptionPlaceholder={newOptionPlaceholder}
setNewOptionPlaceholder={setNewOptionPlaceholder}
newOptionDefaultValue={newOptionDefaultValue}
setNewOptionDefaultValue={setNewOptionDefaultValue}
editingOptionType={editingOptionType}
attributeSubTabs={attributeSubTabs}
attributeColumns={attributeColumns}
handleAddOption={handleAddOption}
/>
<ColumnManageDialog
isOpen={isColumnManageDialogOpen}
setIsOpen={setIsColumnManageDialogOpen}
managingColumnType={managingColumnType}
attributeSubTabs={attributeSubTabs}
attributeColumns={attributeColumns}
setAttributeColumns={setAttributeColumns}
newColumnName={newColumnName}
setNewColumnName={setNewColumnName}
newColumnKey={newColumnKey}
setNewColumnKey={setNewColumnKey}
newColumnType={newColumnType}
setNewColumnType={setNewColumnType}
newColumnRequired={newColumnRequired}
setNewColumnRequired={setNewColumnRequired}
/>
<PathEditDialog
editingPathPageId={editingPathPageId}
setEditingPathPageId={setEditingPathPageId}
editingAbsolutePath={editingAbsolutePath}
setEditingAbsolutePath={setEditingAbsolutePath}
updateItemPage={updateItemPage}
trackChange={() => {}}
/>
<PageDialog
isPageDialogOpen={isPageDialogOpen}
setIsPageDialogOpen={setIsPageDialogOpen}
newPageName={newPageName}
setNewPageName={setNewPageName}
newPageItemType={newPageItemType}
setNewPageItemType={setNewPageItemTypeWrapper}
handleAddPage={handleAddPage}
/>
<SectionDialog
isSectionDialogOpen={isSectionDialogOpen}
setIsSectionDialogOpen={setIsSectionDialogOpen}
newSectionType={newSectionType}
setNewSectionType={setNewSectionType}
newSectionTitle={newSectionTitle}
setNewSectionTitle={setNewSectionTitle}
newSectionDescription={newSectionDescription}
setNewSectionDescription={setNewSectionDescription}
handleAddSection={handleAddSectionWrapper}
sectionInputMode={sectionInputMode}
setSectionInputMode={setSectionInputMode}
sectionTemplates={sectionTemplates}
selectedTemplateId={selectedSectionTemplateId}
setSelectedTemplateId={setSelectedSectionTemplateId}
handleLinkTemplate={handleLinkTemplateWrapper}
/>
{/* 항목 추가/수정 다이얼로그 - 데스크톱 */}
{!isMobile && (
<FieldDialog
isOpen={isFieldDialogOpen}
onOpenChange={setIsFieldDialogOpen}
editingFieldId={editingFieldId}
setEditingFieldId={setEditingFieldId}
fieldInputMode={fieldInputMode}
setFieldInputMode={setFieldInputMode}
showMasterFieldList={showMasterFieldList}
setShowMasterFieldList={setShowMasterFieldList}
selectedMasterFieldId={selectedMasterFieldId}
setSelectedMasterFieldId={setSelectedMasterFieldId}
textboxColumns={textboxColumns}
setTextboxColumns={setTextboxColumns}
newFieldConditionEnabled={newFieldConditionEnabled}
setNewFieldConditionEnabled={setNewFieldConditionEnabled}
newFieldConditionTargetType={newFieldConditionTargetType}
setNewFieldConditionTargetType={setNewFieldConditionTargetType}
newFieldConditionFields={newFieldConditionFields}
setNewFieldConditionFields={setNewFieldConditionFields}
newFieldConditionSections={newFieldConditionSections}
setNewFieldConditionSections={setNewFieldConditionSections}
tempConditionValue={tempConditionValue}
setTempConditionValue={setTempConditionValue}
newFieldName={newFieldName}
setNewFieldName={setNewFieldName}
newFieldKey={newFieldKey}
setNewFieldKey={setNewFieldKey}
newFieldInputType={newFieldInputType}
setNewFieldInputType={setNewFieldInputType}
newFieldRequired={newFieldRequired}
setNewFieldRequired={setNewFieldRequired}
newFieldDescription={newFieldDescription}
setNewFieldDescription={setNewFieldDescription}
newFieldOptions={newFieldOptions}
setNewFieldOptions={setNewFieldOptions}
selectedSectionForField={selectedPage?.sections.find(s => s.id === selectedSectionForField) || null}
selectedPage={selectedPage || null}
itemMasterFields={itemMasterFields}
handleAddField={handleAddFieldWrapper}
setIsColumnDialogOpen={setIsColumnDialogOpen}
setEditingColumnId={setEditingColumnId}
setColumnName={setColumnName}
setColumnKey={setColumnKey}
/>
)}
{/* 항목 추가/수정 다이얼로그 - 모바일 (바텀시트) */}
{isMobile && (
<FieldDrawer
isOpen={isFieldDialogOpen}
onOpenChange={setIsFieldDialogOpen}
editingFieldId={editingFieldId}
setEditingFieldId={setEditingFieldId}
fieldInputMode={fieldInputMode}
setFieldInputMode={setFieldInputMode}
showMasterFieldList={showMasterFieldList}
setShowMasterFieldList={setShowMasterFieldList}
selectedMasterFieldId={selectedMasterFieldId}
setSelectedMasterFieldId={setSelectedMasterFieldId}
textboxColumns={textboxColumns}
setTextboxColumns={setTextboxColumns}
newFieldConditionEnabled={newFieldConditionEnabled}
setNewFieldConditionEnabled={setNewFieldConditionEnabled}
newFieldConditionTargetType={newFieldConditionTargetType}
setNewFieldConditionTargetType={setNewFieldConditionTargetType}
newFieldConditionFields={newFieldConditionFields}
setNewFieldConditionFields={setNewFieldConditionFields}
newFieldConditionSections={newFieldConditionSections}
setNewFieldConditionSections={setNewFieldConditionSections}
tempConditionValue={tempConditionValue}
setTempConditionValue={setTempConditionValue}
newFieldName={newFieldName}
setNewFieldName={setNewFieldName}
newFieldKey={newFieldKey}
setNewFieldKey={setNewFieldKey}
newFieldInputType={newFieldInputType}
setNewFieldInputType={setNewFieldInputType}
newFieldRequired={newFieldRequired}
setNewFieldRequired={setNewFieldRequired}
newFieldDescription={newFieldDescription}
setNewFieldDescription={setNewFieldDescription}
newFieldOptions={newFieldOptions}
setNewFieldOptions={setNewFieldOptions}
selectedSectionForField={selectedPage?.sections.find(s => s.id === selectedSectionForField) || null}
selectedPage={selectedPage || null}
itemMasterFields={itemMasterFields}
handleAddField={handleAddFieldWrapper}
setIsColumnDialogOpen={setIsColumnDialogOpen}
setEditingColumnId={setEditingColumnId}
setColumnName={setColumnName}
setColumnKey={setColumnKey}
/>
)}
{/* 텍스트박스 컬럼 추가/수정 다이얼로그 */}
<ColumnDialog
isColumnDialogOpen={isColumnDialogOpen}
setIsColumnDialogOpen={setIsColumnDialogOpen}
editingColumnId={editingColumnId}
setEditingColumnId={setEditingColumnId}
columnName={columnName}
setColumnName={setColumnName}
columnKey={columnKey}
setColumnKey={setColumnKey}
textboxColumns={textboxColumns}
setTextboxColumns={setTextboxColumns}
/>
<MasterFieldDialog
isMasterFieldDialogOpen={isMasterFieldDialogOpen}
setIsMasterFieldDialogOpen={setIsMasterFieldDialogOpen}
editingMasterFieldId={editingMasterFieldId}
setEditingMasterFieldId={setEditingMasterFieldId}
newMasterFieldName={newMasterFieldName}
setNewMasterFieldName={setNewMasterFieldName}
newMasterFieldKey={newMasterFieldKey}
setNewMasterFieldKey={setNewMasterFieldKey}
newMasterFieldInputType={newMasterFieldInputType}
setNewMasterFieldInputType={setNewMasterFieldInputType}
newMasterFieldRequired={newMasterFieldRequired}
setNewMasterFieldRequired={setNewMasterFieldRequired}
newMasterFieldCategory={newMasterFieldCategory}
setNewMasterFieldCategory={setNewMasterFieldCategory}
newMasterFieldDescription={newMasterFieldDescription}
setNewMasterFieldDescription={setNewMasterFieldDescription}
newMasterFieldOptions={newMasterFieldOptions}
setNewMasterFieldOptions={setNewMasterFieldOptions}
newMasterFieldAttributeType={newMasterFieldAttributeType}
setNewMasterFieldAttributeType={setNewMasterFieldAttributeType}
newMasterFieldMultiColumn={newMasterFieldMultiColumn}
setNewMasterFieldMultiColumn={setNewMasterFieldMultiColumn}
newMasterFieldColumnCount={newMasterFieldColumnCount}
setNewMasterFieldColumnCount={setNewMasterFieldColumnCount}
newMasterFieldColumnNames={newMasterFieldColumnNames}
setNewMasterFieldColumnNames={setNewMasterFieldColumnNames}
handleUpdateMasterField={handleUpdateMasterField}
handleAddMasterField={handleAddMasterField}
/>
<SectionTemplateDialog
isSectionTemplateDialogOpen={isSectionTemplateDialogOpen}
setIsSectionTemplateDialogOpen={setIsSectionTemplateDialogOpen}
editingSectionTemplateId={editingSectionTemplateId}
setEditingSectionTemplateId={setEditingSectionTemplateId}
newSectionTemplateTitle={newSectionTemplateTitle}
setNewSectionTemplateTitle={setNewSectionTemplateTitle}
newSectionTemplateDescription={newSectionTemplateDescription}
setNewSectionTemplateDescription={setNewSectionTemplateDescription}
newSectionTemplateCategory={newSectionTemplateCategory}
setNewSectionTemplateCategory={setNewSectionTemplateCategory}
newSectionTemplateType={newSectionTemplateType}
setNewSectionTemplateType={setNewSectionTemplateType}
handleUpdateSectionTemplate={handleUpdateSectionTemplate}
handleAddSectionTemplate={handleAddSectionTemplate}
/>
<TemplateFieldDialog
isTemplateFieldDialogOpen={isTemplateFieldDialogOpen}
setIsTemplateFieldDialogOpen={setIsTemplateFieldDialogOpen}
editingTemplateFieldId={editingTemplateFieldId}
setEditingTemplateFieldId={setEditingTemplateFieldId}
templateFieldName={templateFieldName}
setTemplateFieldName={setTemplateFieldName}
templateFieldKey={templateFieldKey}
setTemplateFieldKey={setTemplateFieldKey}
templateFieldInputType={templateFieldInputType}
setTemplateFieldInputType={setTemplateFieldInputType}
templateFieldRequired={templateFieldRequired}
setTemplateFieldRequired={setTemplateFieldRequired}
templateFieldOptions={templateFieldOptions}
setTemplateFieldOptions={setTemplateFieldOptions}
templateFieldDescription={templateFieldDescription}
setTemplateFieldDescription={setTemplateFieldDescription}
templateFieldMultiColumn={templateFieldMultiColumn}
setTemplateFieldMultiColumn={setTemplateFieldMultiColumn}
templateFieldColumnCount={templateFieldColumnCount}
setTemplateFieldColumnCount={setTemplateFieldColumnCount}
templateFieldColumnNames={templateFieldColumnNames}
setTemplateFieldColumnNames={setTemplateFieldColumnNames}
handleAddTemplateField={handleAddTemplateField}
// 마스터 항목 관련 props
itemMasterFields={itemMasterFields}
templateFieldInputMode={templateFieldInputMode}
setTemplateFieldInputMode={setTemplateFieldInputMode}
showMasterFieldList={templateFieldShowMasterFieldList}
setShowMasterFieldList={setTemplateFieldShowMasterFieldList}
selectedMasterFieldId={templateFieldSelectedMasterFieldId}
setSelectedMasterFieldId={setTemplateFieldSelectedMasterFieldId}
/>
<LoadTemplateDialog
isLoadTemplateDialogOpen={isLoadTemplateDialogOpen}
setIsLoadTemplateDialogOpen={setIsLoadTemplateDialogOpen}
sectionTemplates={sectionTemplates}
selectedTemplateId={selectedTemplateId}
setSelectedTemplateId={setSelectedTemplateId}
handleLoadTemplate={handleLoadTemplateWrapper}
/>
</PageLayout>
);
}