버튼별로 데이터 필터링기능

This commit is contained in:
kjs
2025-12-16 18:02:08 +09:00
parent a73b37f558
commit d6f40f3cd3
12 changed files with 909 additions and 113 deletions

View File

@@ -1,13 +1,24 @@
"use client";
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useCallback, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Plus, Star, Loader2 } from "lucide-react";
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<string, any> | null;
config: RelatedDataButtonsConfig | null;
};
}
}
interface RelatedDataButtonsComponentProps {
config: RelatedDataButtonsConfig;
className?: string;
@@ -21,12 +32,27 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
}) => {
const [buttons, setButtons] = useState<ButtonItem[]>([]);
const [selectedId, setSelectedId] = useState<string | null>(null);
const [selectedItem, setSelectedItem] = useState<ButtonItem | null>(null);
const [loading, setLoading] = useState(false);
const [masterData, setMasterData] = useState<Record<string, any> | 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) {
@@ -89,6 +115,7 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
const defaultItem = items.find(item => item.isDefault);
const targetItem = defaultItem || items[0];
setSelectedId(targetItem.id);
setSelectedItem(targetItem);
emitSelection(targetItem);
}
}
@@ -104,6 +131,7 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
useEffect(() => {
if (masterData) {
setSelectedId(null); // 마스터 변경 시 선택 초기화
setSelectedItem(null);
loadButtons();
}
}, [masterData, loadButtons]);
@@ -134,9 +162,82 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
// 버튼 클릭 핸들러
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<string, any> = {};
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;
@@ -177,6 +278,7 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
const headerConfig = config.headerDisplay;
const addButtonConfig = config.addButton;
const modalLinkConfig = config.modalLink;
return (
<div className={cn("rounded-lg border bg-card", className)} style={style}>
@@ -198,18 +300,34 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
)}
</div>
{/* 헤더 위치 추가 버튼 */}
{addButtonConfig?.show && addButtonConfig?.position === "header" && (
<Button
variant="default"
size="sm"
onClick={handleAddClick}
className="bg-blue-600 hover:bg-blue-700"
>
<Plus className="mr-1 h-4 w-4" />
{addButtonConfig.label || "버전 추가"}
</Button>
)}
<div className="flex items-center gap-2">
{/* 모달 링크 버튼 (헤더 위치) */}
{modalLinkConfig?.enabled && modalLinkConfig?.triggerType === "button" && modalLinkConfig?.buttonPosition === "header" && (
<Button
variant="outline"
size="sm"
onClick={handleModalLinkClick}
disabled={!selectedItem}
title={!selectedItem ? "버튼을 먼저 선택하세요" : ""}
>
<ExternalLink className="mr-1 h-4 w-4" />
{modalLinkConfig.buttonLabel || "상세 추가"}
</Button>
)}
{/* 헤더 위치 추가 버튼 */}
{addButtonConfig?.show && addButtonConfig?.position === "header" && (
<Button
variant="default"
size="sm"
onClick={handleAddClick}
className="bg-blue-600 hover:bg-blue-700"
>
<Plus className="mr-1 h-4 w-4" />
{addButtonConfig.label || "버전 추가"}
</Button>
)}
</div>
</div>
)}
@@ -258,6 +376,20 @@ export const RelatedDataButtonsComponent: React.FC<RelatedDataButtonsComponentPr
</Button>
))}
{/* 모달 링크 버튼 (인라인 위치) */}
{modalLinkConfig?.enabled && modalLinkConfig?.triggerType === "button" && modalLinkConfig?.buttonPosition !== "header" && (
<Button
variant="outline"
size={config.buttonStyle?.size || "default"}
onClick={handleModalLinkClick}
disabled={!selectedItem}
title={!selectedItem ? "버튼을 먼저 선택하세요" : ""}
>
<ExternalLink className="mr-1 h-4 w-4" />
{modalLinkConfig.buttonLabel || "상세 추가"}
</Button>
)}
{/* 인라인 추가 버튼 */}
{addButtonConfig?.show && addButtonConfig?.position !== "header" && (
<Button