common/feat/dashboard-map #287

Merged
hyeonsu merged 2 commits from common/feat/dashboard-map into main 2025-12-15 11:28:22 +09:00
1 changed files with 110 additions and 20 deletions

View File

@ -1506,6 +1506,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
tableName: tableConfig.selectedTable,
selectedLeftData: splitPanelContext?.selectedLeftData,
linkedFilters: splitPanelContext?.linkedFilters,
splitPanelPosition: splitPanelPosition,
});
if (splitPanelContext) {
@ -1537,6 +1538,39 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
linkedFilterValues[key] = value;
}
}
// 🆕 자동 컬럼 매칭: linkedFilters가 설정되어 있지 않아도
// 우측 화면(splitPanelPosition === "right")이고 좌측 데이터가 선택되어 있으면
// 동일한 컬럼명이 있는 경우 자동으로 필터링 적용
if (
splitPanelPosition === "right" &&
hasSelectedLeftData &&
Object.keys(linkedFilterValues).length === 0 &&
!hasLinkedFiltersConfigured
) {
const leftData = splitPanelContext.selectedLeftData!;
const tableColumns = (tableConfig.columns || []).map((col) => col.columnName);
// 좌측 데이터의 컬럼 중 현재 테이블에 동일한 컬럼이 있는지 확인
for (const [colName, colValue] of Object.entries(leftData)) {
// null, undefined, 빈 문자열 제외
if (colValue === null || colValue === undefined || colValue === "") continue;
// id, objid 등 기본 키는 제외 (너무 일반적인 컬럼명)
if (colName === "id" || colName === "objid" || colName === "company_code") continue;
// 현재 테이블에 동일한 컬럼이 있는지 확인
if (tableColumns.includes(colName)) {
linkedFilterValues[colName] = colValue;
hasLinkedFiltersConfigured = true;
console.log(`🔗 [TableList] 자동 컬럼 매칭: ${colName} = ${colValue}`);
}
}
if (Object.keys(linkedFilterValues).length > 0) {
console.log("🔗 [TableList] 자동 컬럼 매칭 필터 적용:", linkedFilterValues);
}
}
if (Object.keys(linkedFilterValues).length > 0) {
console.log("🔗 [TableList] 연결 필터 적용:", linkedFilterValues);
}
@ -1749,7 +1783,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
searchTerm,
searchValues,
isDesignMode,
splitPanelContext?.selectedLeftData, // 🆕 연결 필터 변경 시 재조회
// 🆕 우측 화면일 때만 selectedLeftData 변경에 반응 (좌측 테이블은 재조회 불필요)
splitPanelPosition,
currentSplitPosition,
splitPanelContext?.selectedLeftData,
]);
const fetchTableDataDebounced = useCallback(
@ -2059,7 +2096,18 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 🆕 분할 패널 컨텍스트에 선택된 데이터 저장 (좌측 화면인 경우)
// disableAutoDataTransfer가 true이면 자동 전달 비활성화 (버튼 클릭으로만 전달)
if (splitPanelContext && splitPanelPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
// currentSplitPosition을 사용하여 정확한 위치 확인 (splitPanelPosition이 없을 수 있음)
const effectiveSplitPosition = splitPanelPosition || currentSplitPosition;
console.log("🔗 [TableList] 행 클릭 - 분할 패널 위치 확인:", {
splitPanelPosition,
currentSplitPosition,
effectiveSplitPosition,
hasSplitPanelContext: !!splitPanelContext,
disableAutoDataTransfer: splitPanelContext?.disableAutoDataTransfer,
});
if (splitPanelContext && effectiveSplitPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
if (!isCurrentlySelected) {
// 선택된 경우: 데이터 저장
splitPanelContext.setSelectedLeftData(row);
@ -2077,12 +2125,57 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
console.log("행 클릭:", { row, index, isSelected: !isCurrentlySelected });
};
// 🆕 셀 클릭 핸들러 (포커스 설정)
// 🆕 셀 클릭 핸들러 (포커스 설정 + 행 선택)
const handleCellClick = (rowIndex: number, colIndex: number, e: React.MouseEvent) => {
e.stopPropagation();
setFocusedCell({ rowIndex, colIndex });
// 테이블 컨테이너에 포커스 설정 (키보드 이벤트 수신용)
tableContainerRef.current?.focus();
// 🆕 분할 패널 내에서 셀 클릭 시에도 해당 행 선택 처리
// filteredData에서 해당 행의 데이터 가져오기
const row = filteredData[rowIndex];
if (!row) return;
const rowKey = getRowKey(row, rowIndex);
const isCurrentlySelected = selectedRows.has(rowKey);
// 분할 패널 컨텍스트가 있고, 좌측 화면인 경우에만 행 선택 및 데이터 전달
const effectiveSplitPosition = splitPanelPosition || currentSplitPosition;
console.log("🔗 [TableList] 셀 클릭 - 분할 패널 위치 확인:", {
rowIndex,
colIndex,
splitPanelPosition,
currentSplitPosition,
effectiveSplitPosition,
hasSplitPanelContext: !!splitPanelContext,
isCurrentlySelected,
});
if (splitPanelContext && effectiveSplitPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
// 이미 선택된 행과 다른 행을 클릭한 경우에만 처리
if (!isCurrentlySelected) {
// 기존 선택 해제하고 새 행 선택
setSelectedRows(new Set([rowKey]));
setIsAllSelected(false);
// 분할 패널 컨텍스트에 데이터 저장
splitPanelContext.setSelectedLeftData(row);
console.log("🔗 [TableList] 셀 클릭으로 분할 패널 좌측 데이터 저장:", {
row,
parentDataMapping: splitPanelContext.parentDataMapping,
});
// onSelectedRowsChange 콜백 호출
if (onSelectedRowsChange) {
onSelectedRowsChange([rowKey], [row], sortColumn || undefined, sortDirection);
}
if (onFormDataChange) {
onFormDataChange({ selectedRows: [rowKey], selectedRowsData: [row] });
}
}
}
};
// 🆕 셀 더블클릭 핸들러 (편집 모드 진입) - visibleColumns 정의 후 사용
@ -4066,13 +4159,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 📎 첨부파일 타입: 파일 아이콘과 개수 표시
// 컬럼명이 'attachments'를 포함하거나, inputType이 file/attachment인 경우
const isAttachmentColumn =
inputType === "file" ||
inputType === "attachment" ||
const isAttachmentColumn =
inputType === "file" ||
inputType === "attachment" ||
column.columnName === "attachments" ||
column.columnName?.toLowerCase().includes("attachment") ||
column.columnName?.toLowerCase().includes("file");
if (isAttachmentColumn) {
// JSONB 배열 또는 JSON 문자열 파싱
let files: any[] = [];
@ -4098,21 +4191,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 파일 이름 표시 (여러 개면 쉼표로 구분)
const { Paperclip } = require("lucide-react");
const fileNames = files.map((f: any) => f.realFileName || f.real_file_name || f.name || "파일").join(", ");
return (
<div className="flex items-center gap-1.5 text-sm max-w-full">
<Paperclip className="h-4 w-4 text-gray-500 flex-shrink-0" />
<span
className="text-blue-600 truncate"
title={fileNames}
>
<div className="flex max-w-full items-center gap-1.5 text-sm">
<Paperclip className="h-4 w-4 flex-shrink-0 text-gray-500" />
<span className="truncate text-blue-600" title={fileNames}>
{fileNames}
</span>
{files.length > 1 && (
<span className="text-muted-foreground text-xs flex-shrink-0">
({files.length})
</span>
)}
{files.length > 1 && <span className="text-muted-foreground flex-shrink-0 text-xs">({files.length})</span>}
</div>
);
}
@ -4677,6 +4763,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
fetchTableLabel();
}, [tableConfig.selectedTable, fetchColumnLabels, fetchTableLabel]);
// 🆕 우측 화면일 때만 selectedLeftData 변경에 반응하도록 변수 생성
const isRightPanel = splitPanelPosition === "right" || currentSplitPosition === "right";
const selectedLeftDataForRightPanel = isRightPanel ? splitPanelContext?.selectedLeftData : null;
useEffect(() => {
// console.log("🔍 [TableList] useEffect 실행 - 데이터 조회 트리거", {
// isDesignMode,
@ -4700,7 +4790,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
refreshKey,
refreshTrigger, // 강제 새로고침 트리거
isDesignMode,
splitPanelContext?.selectedLeftData, // 🆕 좌측 데이터 선택 변경 시 데이터 새로고침
selectedLeftDataForRightPanel, // 🆕 우측 화면일 때만 좌측 데이터 선택 변경 시 데이터 새로고침
// fetchTableDataDebounced 제거: useCallback 재생성으로 인한 무한 루프 방지
]);