화면간 데이터 전달기능 구현
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import React, { useEffect, useState, useMemo, useCallback } from "react";
|
||||
import { ComponentRendererProps } from "@/types/component";
|
||||
import { CardDisplayConfig } from "./types";
|
||||
import { tableTypeApi } from "@/lib/api/screen";
|
||||
@@ -8,6 +8,9 @@ import { filterDOMProps } from "@/lib/utils/domPropsFilter";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useScreenContextOptional } from "@/contexts/ScreenContext";
|
||||
import { useSplitPanelContext } from "@/contexts/SplitPanelContext";
|
||||
import { useModalDataStore } from "@/stores/modalDataStore";
|
||||
|
||||
export interface CardDisplayComponentProps extends ComponentRendererProps {
|
||||
config?: CardDisplayConfig;
|
||||
@@ -38,13 +41,18 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
||||
tableColumns = [],
|
||||
...props
|
||||
}) => {
|
||||
// 컨텍스트 (선택적 - 디자인 모드에서는 없을 수 있음)
|
||||
const screenContext = useScreenContextOptional();
|
||||
const splitPanelContext = useSplitPanelContext();
|
||||
const splitPanelPosition = screenContext?.splitPanelPosition;
|
||||
|
||||
// 테이블 데이터 상태 관리
|
||||
const [loadedTableData, setLoadedTableData] = useState<any[]>([]);
|
||||
const [loadedTableColumns, setLoadedTableColumns] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 선택된 카드 상태
|
||||
const [selectedCardId, setSelectedCardId] = useState<string | number | null>(null);
|
||||
// 선택된 카드 상태 (Set으로 변경하여 테이블 리스트와 동일하게)
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
||||
|
||||
// 상세보기 모달 상태
|
||||
const [viewModalOpen, setViewModalOpen] = useState(false);
|
||||
@@ -199,38 +207,132 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
||||
|
||||
// 표시할 데이터 결정 (로드된 테이블 데이터 우선 사용)
|
||||
const displayData = useMemo(() => {
|
||||
// console.log("📋 CardDisplay: displayData 결정 중", {
|
||||
// dataSource: componentConfig.dataSource,
|
||||
// loadedTableDataLength: loadedTableData.length,
|
||||
// tableDataLength: tableData.length,
|
||||
// staticDataLength: componentConfig.staticData?.length || 0,
|
||||
// });
|
||||
|
||||
// 로드된 테이블 데이터가 있으면 항상 우선 사용 (dataSource 설정 무시)
|
||||
if (loadedTableData.length > 0) {
|
||||
// console.log("📋 CardDisplay: 로드된 테이블 데이터 사용", loadedTableData.slice(0, 2));
|
||||
return loadedTableData;
|
||||
}
|
||||
|
||||
// props로 전달받은 테이블 데이터가 있으면 사용
|
||||
if (tableData.length > 0) {
|
||||
// console.log("📋 CardDisplay: props 테이블 데이터 사용", tableData.slice(0, 2));
|
||||
return tableData;
|
||||
}
|
||||
|
||||
if (componentConfig.staticData && componentConfig.staticData.length > 0) {
|
||||
// console.log("📋 CardDisplay: 정적 데이터 사용", componentConfig.staticData.slice(0, 2));
|
||||
return componentConfig.staticData;
|
||||
}
|
||||
|
||||
// 데이터가 없으면 빈 배열 반환
|
||||
// console.log("📋 CardDisplay: 표시할 데이터가 없음");
|
||||
return [];
|
||||
}, [componentConfig.dataSource, loadedTableData, tableData, componentConfig.staticData]);
|
||||
|
||||
// 실제 사용할 테이블 컬럼 정보 (로드된 컬럼 우선 사용)
|
||||
const actualTableColumns = loadedTableColumns.length > 0 ? loadedTableColumns : tableColumns;
|
||||
|
||||
// 카드 ID 가져오기 함수 (훅은 조기 리턴 전에 선언)
|
||||
const getCardKey = useCallback((data: any, index: number): string => {
|
||||
return String(data.id || data.objid || data.ID || index);
|
||||
}, []);
|
||||
|
||||
// 카드 선택 핸들러 (테이블 리스트와 동일한 로직)
|
||||
const handleCardSelection = useCallback((cardKey: string, data: any, checked: boolean) => {
|
||||
const newSelectedRows = new Set(selectedRows);
|
||||
if (checked) {
|
||||
newSelectedRows.add(cardKey);
|
||||
} else {
|
||||
newSelectedRows.delete(cardKey);
|
||||
}
|
||||
setSelectedRows(newSelectedRows);
|
||||
|
||||
// 선택된 카드 데이터 계산
|
||||
const selectedRowsData = displayData.filter((item, index) =>
|
||||
newSelectedRows.has(getCardKey(item, index))
|
||||
);
|
||||
|
||||
// onFormDataChange 호출
|
||||
if (onFormDataChange) {
|
||||
onFormDataChange({
|
||||
selectedRows: Array.from(newSelectedRows),
|
||||
selectedRowsData,
|
||||
});
|
||||
}
|
||||
|
||||
// modalDataStore에 선택된 데이터 저장
|
||||
const tableNameToUse = componentConfig.dataSource?.tableName || tableName;
|
||||
if (tableNameToUse && selectedRowsData.length > 0) {
|
||||
const modalItems = selectedRowsData.map((row, idx) => ({
|
||||
id: getCardKey(row, idx),
|
||||
originalData: row,
|
||||
additionalData: {},
|
||||
}));
|
||||
useModalDataStore.getState().setData(tableNameToUse, modalItems);
|
||||
console.log("✅ [CardDisplay] modalDataStore에 데이터 저장:", {
|
||||
dataSourceId: tableNameToUse,
|
||||
count: modalItems.length,
|
||||
});
|
||||
} else if (tableNameToUse && selectedRowsData.length === 0) {
|
||||
useModalDataStore.getState().clearData(tableNameToUse);
|
||||
console.log("🗑️ [CardDisplay] modalDataStore 데이터 제거:", tableNameToUse);
|
||||
}
|
||||
|
||||
// 분할 패널 컨텍스트에 선택된 데이터 저장 (좌측 화면인 경우)
|
||||
if (splitPanelContext && splitPanelPosition === "left") {
|
||||
if (checked) {
|
||||
splitPanelContext.setSelectedLeftData(data);
|
||||
console.log("🔗 [CardDisplay] 분할 패널 좌측 데이터 저장:", {
|
||||
data,
|
||||
parentDataMapping: splitPanelContext.parentDataMapping,
|
||||
});
|
||||
} else if (newSelectedRows.size === 0) {
|
||||
splitPanelContext.setSelectedLeftData(null);
|
||||
console.log("🔗 [CardDisplay] 분할 패널 좌측 데이터 초기화");
|
||||
}
|
||||
}
|
||||
}, [selectedRows, displayData, getCardKey, onFormDataChange, componentConfig.dataSource?.tableName, tableName, splitPanelContext, splitPanelPosition]);
|
||||
|
||||
const handleCardClick = useCallback((data: any, index: number) => {
|
||||
const cardKey = getCardKey(data, index);
|
||||
const isCurrentlySelected = selectedRows.has(cardKey);
|
||||
|
||||
// 선택 토글
|
||||
handleCardSelection(cardKey, data, !isCurrentlySelected);
|
||||
|
||||
if (componentConfig.onCardClick) {
|
||||
componentConfig.onCardClick(data);
|
||||
}
|
||||
}, [getCardKey, selectedRows, handleCardSelection, componentConfig.onCardClick]);
|
||||
|
||||
// DataProvidable 인터페이스 구현 (테이블 리스트와 동일)
|
||||
const dataProvider = useMemo(() => ({
|
||||
componentId: component.id,
|
||||
componentType: "card-display" as const,
|
||||
|
||||
getSelectedData: () => {
|
||||
const selectedData = displayData.filter((item, index) =>
|
||||
selectedRows.has(getCardKey(item, index))
|
||||
);
|
||||
return selectedData;
|
||||
},
|
||||
|
||||
getAllData: () => {
|
||||
return displayData;
|
||||
},
|
||||
|
||||
clearSelection: () => {
|
||||
setSelectedRows(new Set());
|
||||
},
|
||||
}), [component.id, displayData, selectedRows, getCardKey]);
|
||||
|
||||
// ScreenContext에 데이터 제공자로 등록
|
||||
useEffect(() => {
|
||||
if (screenContext && component.id) {
|
||||
screenContext.registerDataProvider(component.id, dataProvider);
|
||||
|
||||
return () => {
|
||||
screenContext.unregisterDataProvider(component.id);
|
||||
};
|
||||
}
|
||||
}, [screenContext, component.id, dataProvider]);
|
||||
|
||||
// 로딩 중인 경우 로딩 표시
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -323,20 +425,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
||||
onClick?.();
|
||||
};
|
||||
|
||||
const handleCardClick = (data: any) => {
|
||||
const cardId = data.id || data.objid || data.ID;
|
||||
// 이미 선택된 카드를 다시 클릭하면 선택 해제
|
||||
if (selectedCardId === cardId) {
|
||||
setSelectedCardId(null);
|
||||
} else {
|
||||
setSelectedCardId(cardId);
|
||||
}
|
||||
|
||||
if (componentConfig.onCardClick) {
|
||||
componentConfig.onCardClick(data);
|
||||
}
|
||||
};
|
||||
|
||||
// DOM 안전한 props만 필터링 (filterDOMProps 유틸리티 사용)
|
||||
const safeDomProps = filterDOMProps(props);
|
||||
|
||||
@@ -425,12 +513,12 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
||||
? getColumnValue(data, componentConfig.columnMapping.imageColumn)
|
||||
: data.avatar || data.image || "";
|
||||
|
||||
const cardId = data.id || data.objid || data.ID || index;
|
||||
const isCardSelected = selectedCardId === cardId;
|
||||
const cardKey = getCardKey(data, index);
|
||||
const isCardSelected = selectedRows.has(cardKey);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={cardId}
|
||||
key={cardKey}
|
||||
style={{
|
||||
...cardStyle,
|
||||
borderColor: isCardSelected ? "#000" : "#e5e7eb",
|
||||
@@ -440,7 +528,7 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
||||
: "0 1px 3px rgba(0, 0, 0, 0.08)",
|
||||
}}
|
||||
className="card-hover group cursor-pointer transition-all duration-150"
|
||||
onClick={() => handleCardClick(data)}
|
||||
onClick={() => handleCardClick(data, index)}
|
||||
>
|
||||
{/* 카드 이미지 */}
|
||||
{componentConfig.cardStyle?.showImage && componentConfig.columnMapping?.imageColumn && (
|
||||
|
||||
Reference in New Issue
Block a user