'use client'; import { useState } from 'react'; import { toast } from 'sonner'; import { useItemMaster } from '@/contexts/ItemMasterContext'; import type { ItemPage, SectionTemplate, TemplateField, BOMItem, ItemMasterField } from '@/contexts/ItemMasterContext'; import { templateService } from '../services'; export interface UseTemplateManagementReturn { // 섹션 템플릿 다이얼로그 상태 isSectionTemplateDialogOpen: boolean; setIsSectionTemplateDialogOpen: (open: boolean) => void; editingSectionTemplateId: number | null; setEditingSectionTemplateId: (id: number | null) => void; // 섹션 템플릿 폼 상태 newSectionTemplateTitle: string; setNewSectionTemplateTitle: (title: string) => void; newSectionTemplateDescription: string; setNewSectionTemplateDescription: (desc: string) => void; newSectionTemplateCategory: string[]; setNewSectionTemplateCategory: React.Dispatch>; newSectionTemplateType: 'fields' | 'bom'; setNewSectionTemplateType: (type: 'fields' | 'bom') => void; // 템플릿 불러오기 다이얼로그 isLoadTemplateDialogOpen: boolean; setIsLoadTemplateDialogOpen: (open: boolean) => void; selectedTemplateId: string | null; setSelectedTemplateId: (id: string | null) => void; // 템플릿 필드 다이얼로그 상태 isTemplateFieldDialogOpen: boolean; setIsTemplateFieldDialogOpen: (open: boolean) => void; currentTemplateId: number | null; setCurrentTemplateId: (id: number | null) => void; editingTemplateFieldId: number | null; setEditingTemplateFieldId: (id: number | null) => void; // 템플릿 필드 폼 상태 templateFieldName: string; setTemplateFieldName: (name: string) => void; templateFieldKey: string; setTemplateFieldKey: (key: string) => void; templateFieldInputType: 'textbox' | 'dropdown' | 'checkbox' | 'number' | 'date' | 'textarea'; setTemplateFieldInputType: (type: 'textbox' | 'dropdown' | 'checkbox' | 'number' | 'date' | 'textarea') => void; templateFieldRequired: boolean; setTemplateFieldRequired: (required: boolean) => void; templateFieldOptions: string; setTemplateFieldOptions: (options: string) => void; templateFieldDescription: string; setTemplateFieldDescription: (desc: string) => void; templateFieldMultiColumn: boolean; setTemplateFieldMultiColumn: (multi: boolean) => void; templateFieldColumnCount: number; setTemplateFieldColumnCount: (count: number) => void; templateFieldColumnNames: string[]; setTemplateFieldColumnNames: React.Dispatch>; // 템플릿 필드 마스터 항목 관련 templateFieldInputMode: 'custom' | 'master'; setTemplateFieldInputMode: (mode: 'custom' | 'master') => void; templateFieldShowMasterFieldList: boolean; setTemplateFieldShowMasterFieldList: (show: boolean) => void; templateFieldSelectedMasterFieldId: string; setTemplateFieldSelectedMasterFieldId: (id: string) => void; // 핸들러 handleAddSectionTemplate: () => void; handleEditSectionTemplate: (template: SectionTemplate) => void; handleUpdateSectionTemplate: () => void; handleDeleteSectionTemplate: (id: number) => void; handleLoadTemplate: (selectedPage: ItemPage | undefined) => void; handleAddTemplateField: () => Promise; handleEditTemplateField: (templateId: number, field: TemplateField) => void; handleDeleteTemplateField: (templateId: number, fieldId: string) => void; handleAddBOMItemToTemplate: (templateId: number, item: Omit) => void; handleUpdateBOMItemInTemplate: (templateId: number, itemId: number, item: Partial) => void; handleDeleteBOMItemFromTemplate: (templateId: number, itemId: number) => void; resetSectionTemplateForm: () => void; resetTemplateFieldForm: () => void; } export function useTemplateManagement(): UseTemplateManagementReturn { const { sectionTemplates, addSectionTemplate, updateSectionTemplate, deleteSectionTemplate, addSectionToPage, addItemMasterField, itemMasterFields, tenantId, // 2025-11-26: sectionsAsTemplates가 itemPages에서 파생되므로 // 섹션 탭에서 수정/삭제 시 실제 섹션 API를 호출해야 함 updateSection, deleteSection, itemPages, // 2025-11-26: 섹션 탭에서 새 섹션 추가 시 독립 섹션으로 생성 createIndependentSection, // 2025-11-27: entity_relationships 기반 필드 연결/해제 linkFieldToSection, unlinkFieldFromSection, independentFields, // 2025-11-27: 필드 수정 API updateField, // 2025-12-01: 섹션에 필드 추가 API (계층구조 탭과 동일한 방식) addFieldToSection, // 2025-12-01: BOM 관리 API (API 기반으로 변경) addBOMItem, updateBOMItem, deleteBOMItem, } = useItemMaster(); // 섹션 템플릿 다이얼로그 상태 const [isSectionTemplateDialogOpen, setIsSectionTemplateDialogOpen] = useState(false); const [editingSectionTemplateId, setEditingSectionTemplateId] = useState(null); // 섹션 템플릿 폼 상태 const [newSectionTemplateTitle, setNewSectionTemplateTitle] = useState(''); const [newSectionTemplateDescription, setNewSectionTemplateDescription] = useState(''); const [newSectionTemplateCategory, setNewSectionTemplateCategory] = useState([]); const [newSectionTemplateType, setNewSectionTemplateType] = useState<'fields' | 'bom'>('fields'); // 템플릿 불러오기 다이얼로그 const [isLoadTemplateDialogOpen, setIsLoadTemplateDialogOpen] = useState(false); const [selectedTemplateId, setSelectedTemplateId] = useState(null); // 템플릿 필드 다이얼로그 상태 const [isTemplateFieldDialogOpen, setIsTemplateFieldDialogOpen] = useState(false); const [currentTemplateId, setCurrentTemplateId] = useState(null); const [editingTemplateFieldId, setEditingTemplateFieldId] = useState(null); // 템플릿 필드 폼 상태 const [templateFieldName, setTemplateFieldName] = useState(''); const [templateFieldKey, setTemplateFieldKey] = useState(''); const [templateFieldInputType, setTemplateFieldInputType] = useState<'textbox' | 'dropdown' | 'checkbox' | 'number' | 'date' | 'textarea'>('textbox'); const [templateFieldRequired, setTemplateFieldRequired] = useState(false); const [templateFieldOptions, setTemplateFieldOptions] = useState(''); const [templateFieldDescription, setTemplateFieldDescription] = useState(''); const [templateFieldMultiColumn, setTemplateFieldMultiColumn] = useState(false); const [templateFieldColumnCount, setTemplateFieldColumnCount] = useState(2); const [templateFieldColumnNames, setTemplateFieldColumnNames] = useState(['컬럼1', '컬럼2']); // 템플릿 필드 마스터 항목 관련 const [templateFieldInputMode, setTemplateFieldInputMode] = useState<'custom' | 'master'>('custom'); const [templateFieldShowMasterFieldList, setTemplateFieldShowMasterFieldList] = useState(false); const [templateFieldSelectedMasterFieldId, setTemplateFieldSelectedMasterFieldId] = useState(''); // 섹션 템플릿 추가 (2025-11-26: 독립 섹션으로 생성하여 sectionsAsTemplates에 반영) const handleAddSectionTemplate = async () => { if (!newSectionTemplateTitle.trim()) { toast.error('섹션 제목을 입력해주세요'); return; } // 2025-11-26: sectionsAsTemplates가 itemPages + independentSections에서 파생되므로 // 독립 섹션으로 생성해야 화면에 바로 반영됨 const newSectionData = { title: newSectionTemplateTitle, type: newSectionTemplateType as 'fields' | 'bom', description: newSectionTemplateDescription || undefined, is_template: true, // 섹션 탭에서 생성된 섹션은 템플릿으로 표시 is_default: false, }; console.log('Adding independent section (from section tab):', newSectionData); try { await createIndependentSection(newSectionData); resetSectionTemplateForm(); toast.success('섹션이 추가되었습니다!'); } catch (error) { console.error('섹션 추가 실패:', error); toast.error('섹션 추가에 실패했습니다.'); } }; // 섹션 템플릿 수정 시작 const handleEditSectionTemplate = (template: SectionTemplate) => { setEditingSectionTemplateId(template.id); setNewSectionTemplateTitle(template.template_name); setNewSectionTemplateDescription(template.description || ''); setNewSectionTemplateCategory(template.category || []); setNewSectionTemplateType(template.section_type === 'BOM' ? 'bom' : 'fields'); setIsSectionTemplateDialogOpen(true); }; // 섹션 템플릿 업데이트 (2025-11-26: sectionsAsTemplates 사용으로 실제 섹션 API 호출) const handleUpdateSectionTemplate = async () => { if (!editingSectionTemplateId || !newSectionTemplateTitle.trim()) { toast.error('섹션 제목을 입력해주세요'); return; } // sectionsAsTemplates가 itemPages에서 파생되므로, 실제 섹션을 업데이트해야 함 const updateData = { title: newSectionTemplateTitle, description: newSectionTemplateDescription || undefined, section_type: (newSectionTemplateType === 'bom' ? 'BOM' : 'BASIC') as 'BASIC' | 'BOM' | 'CUSTOM' }; console.log('Updating section (from template handler):', { id: editingSectionTemplateId, updateData }); try { // updateSection 호출 (템플릿이 아닌 실제 섹션 API) await updateSection(editingSectionTemplateId, updateData); resetSectionTemplateForm(); toast.success('섹션이 수정되었습니다!'); } catch (error) { console.error('섹션 수정 실패:', error); toast.error('섹션 수정에 실패했습니다.'); } }; // 섹션 템플릿 삭제 (2025-11-26: sectionsAsTemplates 사용으로 실제 섹션 API 호출) const handleDeleteSectionTemplate = async (id: number) => { if (confirm('이 섹션을 삭제하시겠습니까?')) { try { // deleteSection 호출 (템플릿이 아닌 실제 섹션 API) await deleteSection(id); toast.success('섹션이 삭제되었습니다!'); } catch (error) { console.error('섹션 삭제 실패:', error); toast.error('섹션 삭제에 실패했습니다.'); } } }; // 템플릿 불러오기 const handleLoadTemplate = (selectedPage: ItemPage | undefined) => { if (!selectedTemplateId || !selectedPage) { toast.error('템플릿을 선택해주세요'); return; } const template = sectionTemplates.find(t => t.id === Number(selectedTemplateId)); if (!template) { toast.error('템플릿을 찾을 수 없습니다'); return; } const newSection = { page_id: selectedPage.id, title: template.template_name, section_type: template.section_type === 'BOM' ? 'BOM' as const : 'BASIC' as const, description: template.description || undefined, order_no: selectedPage.sections.length + 1, is_template: false, is_default: false, is_collapsible: true, is_default_open: true, fields: [], bom_items: template.section_type === 'BOM' ? [] : undefined }; console.log('Loading template to section:', template.template_name, 'newSection:', newSection); addSectionToPage(selectedPage.id, newSection); setSelectedTemplateId(null); setIsLoadTemplateDialogOpen(false); toast.success('섹션이 불러와졌습니다'); }; // 템플릿 필드 추가/수정 (2025-11-27: API 사용으로 변경) // sectionsAsTemplates가 itemPages + independentSections에서 파생되므로 // entity_relationships 기반 연결 API를 사용해야 실시간 반영됨 // 2025-12-01: custom/master 모드 분기 처리 추가 const handleAddTemplateField = async () => { if (!currentTemplateId || !templateFieldName.trim() || !templateFieldKey.trim()) { toast.error('모든 필수 항목을 입력해주세요'); return; } try { // 수정 모드: 기존 필드 속성 업데이트 if (editingTemplateFieldId) { // 2025-11-28: field_key 추가 (백엔드 요청에 포함) const updateData = { field_name: templateFieldName, field_key: templateFieldKey, // 2025-11-28: field_key 추가 field_type: templateFieldInputType, is_required: templateFieldRequired, placeholder: templateFieldDescription || null, options: templateFieldInputType === 'dropdown' && templateFieldOptions.trim() ? templateFieldOptions.split(',').map(o => ({ label: o.trim(), value: o.trim() })) : null, properties: { inputType: templateFieldInputType, required: templateFieldRequired, multiColumn: (templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') ? templateFieldMultiColumn : undefined, columnCount: (templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') && templateFieldMultiColumn ? templateFieldColumnCount : undefined, columnNames: (templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') && templateFieldMultiColumn ? templateFieldColumnNames : undefined, }, }; await updateField(editingTemplateFieldId, updateData); toast.success('항목이 수정되었습니다'); resetTemplateFieldForm(); return; } // 추가 모드: custom/master 모드에 따라 분기 처리 // 2025-12-01: custom 모드에서는 새 필드 생성 후 연결, master 모드에서는 기존 필드 연결 if (templateFieldInputMode === 'master') { // master 모드: 기존 필드를 섹션에 연결 // templateFieldKey는 선택된 필드의 ID (handleSelectMasterField에서 설정됨) const existingField = independentFields.find(f => f.id.toString() === templateFieldKey); if (existingField) { await linkFieldToSection(currentTemplateId, existingField.id); toast.success('항목이 섹션에 연결되었습니다'); } else { toast.error('항목 탭에서 먼저 항목을 생성해주세요'); return; } } else { // custom 모드: 섹션에 직접 필드 추가 (계층구조 탭과 동일한 방식) // 2025-12-01: createIndependentField + linkFieldToSection 대신 addFieldToSection 사용 // POST /sections/{id}/fields API를 사용하여 섹션에 바로 필드 생성 const newFieldData = { section_id: currentTemplateId, master_field_id: null, field_name: templateFieldName, field_key: templateFieldKey, field_type: templateFieldInputType, order_no: 0, is_required: templateFieldRequired, placeholder: templateFieldDescription || null, default_value: null, display_condition: null, validation_rules: null, options: templateFieldInputType === 'dropdown' && templateFieldOptions.trim() ? templateFieldOptions.split(',').map(o => ({ label: o.trim(), value: o.trim() })) : null, properties: { inputType: templateFieldInputType, required: templateFieldRequired, multiColumn: (templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') ? templateFieldMultiColumn : undefined, columnCount: (templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') && templateFieldMultiColumn ? templateFieldColumnCount : undefined, columnNames: (templateFieldInputType === 'textbox' || templateFieldInputType === 'textarea') && templateFieldMultiColumn ? templateFieldColumnNames : undefined, }, }; // 섹션에 필드 추가 (계층구조 탭과 동일) await addFieldToSection(currentTemplateId, newFieldData); toast.success('항목이 섹션에 추가되었습니다'); } resetTemplateFieldForm(); } catch (error) { console.error('항목 처리 실패:', error); toast.error('항목 처리에 실패했습니다'); } }; // 템플릿 필드 수정 시작 // 2025-11-28: field_key 형식 {ID}_{사용자입력}에서 사용자입력 부분만 추출 const handleEditTemplateField = (templateId: number, field: TemplateField) => { setCurrentTemplateId(templateId); setEditingTemplateFieldId(Number(field.id)); setTemplateFieldName(field.name); // 2025-12-01: templateService 사용으로 변경 setTemplateFieldKey(templateService.extractUserInputFromFieldKey(field.fieldKey || '')); setTemplateFieldInputType(field.property.inputType); setTemplateFieldRequired(field.property.required); setTemplateFieldOptions(field.property.options?.join(', ') || ''); setTemplateFieldDescription(field.description || ''); setTemplateFieldMultiColumn(field.property.multiColumn || false); setTemplateFieldColumnCount(field.property.columnCount || 2); setTemplateFieldColumnNames(field.property.columnNames || ['컬럼1', '컬럼2']); setIsTemplateFieldDialogOpen(true); }; // 템플릿 필드 연결 해제 (2025-11-27: entity_relationships 기반으로 변경) // sectionId = templateId (sectionsAsTemplates에서 섹션 ID로 사용) // fieldId = 실제 item_fields의 ID const handleDeleteTemplateField = async (templateId: number, fieldId: string) => { if (!confirm('이 항목의 연결을 해제하시겠습니까?\n(항목 자체는 삭제되지 않고 항목 탭에 유지됩니다)')) return; try { // entity_relationships 기반 연결 해제 API 호출 await unlinkFieldFromSection(templateId, Number(fieldId)); toast.success('항목 연결이 해제되었습니다'); } catch (error) { console.error('항목 연결 해제 실패:', error); toast.error('항목 연결 해제에 실패했습니다.'); } }; // BOM 항목 추가 (2025-12-01: API 기반으로 변경) // templateId = sectionId (sectionsAsTemplates에서 섹션 ID로 사용) const handleAddBOMItemToTemplate = async (templateId: number, item: Omit) => { try { // addBOMItem API 호출 (Context에서 itemPages/independentSections 자동 업데이트) await addBOMItem(templateId, item); // toast는 BOMManagementSection 컴포넌트에서 처리 } catch (error) { console.error('BOM 항목 추가 실패:', error); toast.error('BOM 항목 추가에 실패했습니다'); } }; // BOM 항목 수정 (2025-12-01: API 기반으로 변경) const handleUpdateBOMItemInTemplate = async (templateId: number, itemId: number, item: Partial) => { try { // updateBOMItem API 호출 (Context에서 itemPages/independentSections 자동 업데이트) await updateBOMItem(itemId, item); // toast는 BOMManagementSection 컴포넌트에서 처리 } catch (error) { console.error('BOM 항목 수정 실패:', error); toast.error('BOM 항목 수정에 실패했습니다'); } }; // BOM 항목 삭제 (2025-12-01: API 기반으로 변경) const handleDeleteBOMItemFromTemplate = async (templateId: number, itemId: number) => { try { // deleteBOMItem API 호출 (Context에서 itemPages/independentSections 자동 업데이트) await deleteBOMItem(itemId); // toast는 BOMManagementSection 컴포넌트에서 처리 } catch (error) { console.error('BOM 항목 삭제 실패:', error); toast.error('BOM 항목 삭제에 실패했습니다'); } }; // 섹션 템플릿 폼 초기화 const resetSectionTemplateForm = () => { setEditingSectionTemplateId(null); setNewSectionTemplateTitle(''); setNewSectionTemplateDescription(''); setNewSectionTemplateCategory([]); setNewSectionTemplateType('fields'); setIsSectionTemplateDialogOpen(false); }; // 템플릿 필드 폼 초기화 const resetTemplateFieldForm = () => { setTemplateFieldName(''); setTemplateFieldKey(''); setTemplateFieldInputType('textbox'); setTemplateFieldRequired(false); setTemplateFieldOptions(''); setTemplateFieldDescription(''); setTemplateFieldMultiColumn(false); setTemplateFieldColumnCount(2); setTemplateFieldColumnNames(['컬럼1', '컬럼2']); setEditingTemplateFieldId(null); setTemplateFieldInputMode('custom'); setTemplateFieldShowMasterFieldList(false); setTemplateFieldSelectedMasterFieldId(''); setIsTemplateFieldDialogOpen(false); }; return { // 섹션 템플릿 다이얼로그 상태 isSectionTemplateDialogOpen, setIsSectionTemplateDialogOpen, editingSectionTemplateId, setEditingSectionTemplateId, // 섹션 템플릿 폼 상태 newSectionTemplateTitle, setNewSectionTemplateTitle, newSectionTemplateDescription, setNewSectionTemplateDescription, newSectionTemplateCategory, setNewSectionTemplateCategory, newSectionTemplateType, setNewSectionTemplateType, // 템플릿 불러오기 다이얼로그 isLoadTemplateDialogOpen, setIsLoadTemplateDialogOpen, selectedTemplateId, setSelectedTemplateId, // 템플릿 필드 다이얼로그 상태 isTemplateFieldDialogOpen, setIsTemplateFieldDialogOpen, 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, resetSectionTemplateForm, resetTemplateFieldForm, }; }