"use client"; import React, { useState, useEffect, useCallback, useRef } from "react"; import { Button } from "@/components/ui/button"; import { Plus, Star, Loader2, ExternalLink } from "lucide-react"; import { cn } from "@/lib/utils"; import { useSplitPanelContext } from "@/contexts/SplitPanelContext"; import { dataApi } from "@/lib/api/data"; import type { RelatedDataButtonsConfig, ButtonItem } from "./types"; // 전역 상태: 현재 선택된 버튼 데이터를 외부에서 접근 가능하게 declare global { interface Window { __relatedButtonsSelectedData?: { selectedItem: ButtonItem | null; masterData: Record | null; config: RelatedDataButtonsConfig | null; }; // 🆕 RelatedDataButtons가 대상으로 하는 테이블 목록 (전역 레지스트리) __relatedButtonsTargetTables?: Set; } } // 전역 레지스트리 초기화 if (typeof window !== "undefined" && !window.__relatedButtonsTargetTables) { window.__relatedButtonsTargetTables = new Set(); } interface RelatedDataButtonsComponentProps { config: RelatedDataButtonsConfig; className?: string; style?: React.CSSProperties; } export const RelatedDataButtonsComponent: React.FC = ({ config, className, style, }) => { const [buttons, setButtons] = useState([]); const [selectedId, setSelectedId] = useState(null); const [selectedItem, setSelectedItem] = useState(null); const [loading, setLoading] = useState(false); const [masterData, setMasterData] = useState | null>(null); // SplitPanel Context 연결 const splitPanelContext = useSplitPanelContext(); // 선택된 데이터를 전역 상태에 저장 (외부 버튼에서 접근용) useEffect(() => { window.__relatedButtonsSelectedData = { selectedItem, masterData, config, }; console.log("🔄 [RelatedDataButtons] 전역 상태 업데이트:", { selectedItem, hasConfig: !!config, modalLink: config?.modalLink, }); }, [selectedItem, masterData, config]); // 좌측 패널에서 선택된 데이터 감지 useEffect(() => { if (!splitPanelContext?.selectedLeftData) { setMasterData(null); setButtons([]); setSelectedId(null); setSelectedItem(null); // 🆕 좌측 데이터가 없을 때 대상 테이블에 빈 상태 알림 if (config.events?.targetTable) { window.dispatchEvent(new CustomEvent("related-button-select", { detail: { targetTable: config.events.targetTable, filterColumn: config.events.targetFilterColumn, filterValue: null, // null로 설정하여 빈 상태 표시 selectedData: null, }, })); } return; } setMasterData(splitPanelContext.selectedLeftData); }, [splitPanelContext?.selectedLeftData, config.events]); // 🆕 컴포넌트 마운트 시 대상 테이블에 필터 필요 알림 useEffect(() => { if (config.events?.targetTable) { // 전역 레지스트리에 등록 window.__relatedButtonsTargetTables?.add(config.events.targetTable); // 이벤트도 발생 (이미 마운트된 테이블 컴포넌트를 위해) window.dispatchEvent(new CustomEvent("related-button-register", { detail: { targetTable: config.events.targetTable, filterColumn: config.events.targetFilterColumn, }, })); console.log("📝 [RelatedDataButtons] 대상 테이블에 필터 등록:", config.events.targetTable); } return () => { // 컴포넌트 언마운트 시 등록 해제 if (config.events?.targetTable) { window.__relatedButtonsTargetTables?.delete(config.events.targetTable); window.dispatchEvent(new CustomEvent("related-button-unregister", { detail: { targetTable: config.events.targetTable, }, })); } }; }, [config.events?.targetTable, config.events?.targetFilterColumn]); // 버튼 데이터 로드 const loadButtons = useCallback(async () => { if (!masterData || !config.buttonDataSource?.tableName) { return; } const filterValue = masterData[config.sourceMapping.sourceColumn]; if (!filterValue) { setButtons([]); return; } setLoading(true); try { const { tableName, filterColumn, displayColumn, valueColumn, orderColumn, orderDirection } = config.buttonDataSource; const response = await dataApi.getTableData(tableName, { filters: { [filterColumn]: filterValue }, sortBy: orderColumn || "created_date", sortOrder: (orderDirection?.toLowerCase() || "asc") as "asc" | "desc", size: 50, }); if (response.data && response.data.length > 0) { const defaultConfig = config.buttonStyle?.defaultIndicator; const items: ButtonItem[] = response.data.map((row: Record) => { let isDefault = false; if (defaultConfig?.column) { const val = row[defaultConfig.column]; const checkValue = defaultConfig.value || "Y"; isDefault = val === checkValue || val === true || val === "true"; } return { id: row.id || row[valueColumn || "id"], displayText: row[displayColumn] || row.id, value: row[valueColumn || "id"], isDefault, rawData: row, }; }); setButtons(items); // 자동 선택: 기본 항목 또는 첫 번째 항목 if (config.autoSelectFirst && items.length > 0) { const defaultItem = items.find(item => item.isDefault); const targetItem = defaultItem || items[0]; setSelectedId(targetItem.id); setSelectedItem(targetItem); emitSelection(targetItem); } } } catch (error) { console.error("RelatedDataButtons 데이터 로드 실패:", error); setButtons([]); } finally { setLoading(false); } }, [masterData, config.buttonDataSource, config.sourceMapping, config.buttonStyle, config.autoSelectFirst]); // masterData 변경 시 버튼 로드 useEffect(() => { if (masterData) { setSelectedId(null); // 마스터 변경 시 선택 초기화 setSelectedItem(null); loadButtons(); } }, [masterData, loadButtons]); // 선택 이벤트 발생 const emitSelection = useCallback((item: ButtonItem) => { if (!config.events?.targetTable || !config.events?.targetFilterColumn) { return; } // 커스텀 이벤트 발생 (하위 테이블 필터링용) window.dispatchEvent(new CustomEvent("related-button-select", { detail: { targetTable: config.events.targetTable, filterColumn: config.events.targetFilterColumn, filterValue: item.value, selectedData: item.rawData, }, })); console.log("📌 RelatedDataButtons 선택 이벤트:", { targetTable: config.events.targetTable, filterColumn: config.events.targetFilterColumn, filterValue: item.value, }); }, [config.events]); // 버튼 클릭 핸들러 const handleButtonClick = useCallback((item: ButtonItem) => { setSelectedId(item.id); setSelectedItem(item); emitSelection(item); }, [emitSelection]); // 모달 열기 (선택된 버튼 데이터 전달) const openModalWithSelectedData = useCallback((targetScreenId: number) => { if (!selectedItem) { console.warn("선택된 버튼이 없습니다."); return; } // 데이터 매핑 적용 const initialData: Record = {}; if (config.modalLink?.dataMapping) { config.modalLink.dataMapping.forEach(mapping => { if (mapping.sourceField === "value") { initialData[mapping.targetField] = selectedItem.value; } else if (mapping.sourceField === "id") { initialData[mapping.targetField] = selectedItem.id; } else if (selectedItem.rawData[mapping.sourceField] !== undefined) { initialData[mapping.targetField] = selectedItem.rawData[mapping.sourceField]; } }); } else { // 기본 매핑: id를 routing_version_id로 전달 initialData["routing_version_id"] = selectedItem.value || selectedItem.id; } console.log("📤 RelatedDataButtons 모달 열기:", { targetScreenId, selectedItem, initialData, }); window.dispatchEvent(new CustomEvent("open-screen-modal", { detail: { screenId: targetScreenId, initialData, onSuccess: () => { loadButtons(); // 모달 성공 후 새로고침 }, }, })); }, [selectedItem, config.modalLink, loadButtons]); // 외부 버튼에서 모달 열기 요청 수신 useEffect(() => { const handleExternalModalOpen = (event: CustomEvent) => { const { targetScreenId, componentId } = event.detail || {}; // componentId가 지정되어 있고 현재 컴포넌트가 아니면 무시 if (componentId && componentId !== config.sourceMapping?.sourceTable) { return; } if (targetScreenId && selectedItem) { openModalWithSelectedData(targetScreenId); } }; window.addEventListener("related-buttons-open-modal" as any, handleExternalModalOpen); return () => { window.removeEventListener("related-buttons-open-modal" as any, handleExternalModalOpen); }; }, [selectedItem, config.sourceMapping, openModalWithSelectedData]); // 내부 모달 링크 버튼 클릭 const handleModalLinkClick = useCallback(() => { if (!config.modalLink?.targetScreenId) { console.warn("모달 링크 설정이 없습니다."); return; } openModalWithSelectedData(config.modalLink.targetScreenId); }, [config.modalLink, openModalWithSelectedData]); // 추가 버튼 클릭 const handleAddClick = useCallback(() => { if (!config.addButton?.modalScreenId) return; const filterValue = masterData?.[config.sourceMapping.sourceColumn]; window.dispatchEvent(new CustomEvent("open-screen-modal", { detail: { screenId: config.addButton.modalScreenId, initialData: { [config.buttonDataSource.filterColumn]: filterValue, }, onSuccess: () => { loadButtons(); // 모달 성공 후 새로고침 }, }, })); }, [config.addButton, config.buttonDataSource.filterColumn, config.sourceMapping.sourceColumn, masterData, loadButtons]); // 버튼 variant 계산 const getButtonVariant = useCallback((item: ButtonItem): "default" | "outline" | "secondary" | "ghost" => { if (selectedId === item.id) { return config.buttonStyle?.activeVariant || "default"; } return config.buttonStyle?.variant || "outline"; }, [selectedId, config.buttonStyle]); // 마스터 데이터 없음 if (!masterData) { return (

좌측에서 항목을 선택하세요

); } const headerConfig = config.headerDisplay; const addButtonConfig = config.addButton; const modalLinkConfig = config.modalLink; return (
{/* 헤더 영역 */} {headerConfig?.show !== false && (
{/* 제목 (품목명 등) */} {headerConfig?.titleColumn && masterData[headerConfig.titleColumn] && (

{masterData[headerConfig.titleColumn]}

)} {/* 부제목 (품목코드 등) */} {headerConfig?.subtitleColumn && masterData[headerConfig.subtitleColumn] && (

{masterData[headerConfig.subtitleColumn]}

)}
{/* 모달 링크 버튼 (헤더 위치) */} {modalLinkConfig?.enabled && modalLinkConfig?.triggerType === "button" && modalLinkConfig?.buttonPosition === "header" && ( )} {/* 헤더 위치 추가 버튼 */} {addButtonConfig?.show && addButtonConfig?.position === "header" && ( )}
)} {/* 버튼 영역 */}
{loading ? (
) : buttons.length === 0 ? (

{config.emptyMessage || "데이터가 없습니다"}

{/* 인라인 추가 버튼 (데이터 없을 때) */} {addButtonConfig?.show && addButtonConfig?.position !== "header" && ( )}
) : (
{buttons.map((item) => ( ))} {/* 모달 링크 버튼 (인라인 위치) */} {modalLinkConfig?.enabled && modalLinkConfig?.triggerType === "button" && modalLinkConfig?.buttonPosition !== "header" && ( )} {/* 인라인 추가 버튼 */} {addButtonConfig?.show && addButtonConfig?.position !== "header" && ( )}
)}
); }; export default RelatedDataButtonsComponent;