);
case "map-summary":
return ;
case "list-summary":
return ;
case "risk-alert":
return ;
case "calendar":
return ;
case "status-summary":
return ;
// === 운영/작업 지원 ===
case "todo":
return ;
case "booking-alert":
return ;
case "maintenance":
return ;
case "document":
return ;
case "list":
return ;
// === 차량 관련 (추가 위젯) ===
case "vehicle-status":
return ;
case "vehicle-list":
return ;
case "vehicle-map":
return ;
// === 배송 관련 (추가 위젯) ===
case "delivery-status":
return ;
case "delivery-status-summary":
return ;
case "delivery-today-stats":
return ;
case "cargo-list":
return ;
case "customer-issues":
return ;
// === 기본 fallback ===
default:
return (
❓
알 수 없는 위젯 타입: {element.subtype}
);
}
}
interface DashboardViewerProps {
elements: DashboardElement[];
dashboardId: string;
refreshInterval?: number; // 전체 대시보드 새로고침 간격 (ms)
}
/**
* 대시보드 뷰어 컴포넌트
* - 저장된 대시보드를 읽기 전용으로 표시
* - 실시간 데이터 업데이트
* - 반응형 레이아웃
*/
export function DashboardViewer({ elements, dashboardId, refreshInterval }: DashboardViewerProps) {
const [elementData, setElementData] = useState>({});
const [loadingElements, setLoadingElements] = useState>(new Set());
const [lastRefresh, setLastRefresh] = useState(new Date());
// elements 변경 감지
React.useEffect(() => {
console.log("🎯 DashboardViewer elements 변경됨:", elements.map(el => ({ id: el.id, customTitle: el.customTitle, title: el.title })));
}, [elements]);
// 개별 요소 데이터 로딩
const loadElementData = useCallback(async (element: DashboardElement) => {
if (!element.dataSource?.query || element.type !== "chart") {
return;
}
setLoadingElements((prev) => new Set([...prev, element.id]));
try {
let result;
// 외부 DB vs 현재 DB 분기
if (element.dataSource.connectionType === "external" && element.dataSource.externalConnectionId) {
// 외부 DB
const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection");
const externalResult = await ExternalDbConnectionAPI.executeQuery(
parseInt(element.dataSource.externalConnectionId),
element.dataSource.query,
);
if (!externalResult.success) {
throw new Error(externalResult.message || "외부 DB 쿼리 실행 실패");
}
const data: QueryResult = {
columns: externalResult.data?.[0] ? Object.keys(externalResult.data[0]) : [],
rows: externalResult.data || [],
totalRows: externalResult.data?.length || 0,
executionTime: 0,
};
setElementData((prev) => ({
...prev,
[element.id]: data,
}));
} else {
// 현재 DB
const { dashboardApi } = await import("@/lib/api/dashboard");
result = await dashboardApi.executeQuery(element.dataSource.query);
const data: QueryResult = {
columns: result.columns || [],
rows: result.rows || [],
totalRows: result.rowCount || 0,
executionTime: 0,
};
setElementData((prev) => ({
...prev,
[element.id]: data,
}));
}
} catch (error) {
// 에러 발생 시 무시 (차트는 빈 상태로 표시됨)
} finally {
setLoadingElements((prev) => {
const newSet = new Set(prev);
newSet.delete(element.id);
return newSet;
});
}
}, []);
// 모든 요소 데이터 로딩
const loadAllData = useCallback(async () => {
setLastRefresh(new Date());
const chartElements = elements.filter((el) => el.type === "chart" && el.dataSource?.query);
// 병렬로 모든 차트 데이터 로딩
await Promise.all(chartElements.map((element) => loadElementData(element)));
}, [elements, loadElementData]);
// 초기 데이터 로딩
useEffect(() => {
loadAllData();
}, [loadAllData]);
// 전체 새로고침 간격 설정
useEffect(() => {
if (!refreshInterval || refreshInterval === 0) {
return;
}
const interval = setInterval(loadAllData, refreshInterval);
return () => clearInterval(interval);
}, [refreshInterval, loadAllData]);
// 요소가 없는 경우
if (elements.length === 0) {
return (