import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { useDevices } from '../hooks/useDevices'; import { useSensorData } from '../hooks/useSensorData'; import LoadingSpinner from '../components/LoadingSpinner'; import StatusIndicator from '../components/StatusIndicator'; import SensorCard from '../components/SensorCard'; import SensorChart from '../components/SensorChart'; import { transformSensorDataToChartData, transformSensorDataToCardData, validateChartData, ChartDataPoint, SensorCardData } from '../utils/dataTransformers'; import { isSensorData } from '../utils/typeGuards'; // 안전한 숫자 변환 함수 - 타입 안전성 강화 const safeNumber = (value: any, defaultValue: number = 0): number => { if (value === null || value === undefined) { return defaultValue; } const num = Number(value); if (typeof num === 'number' && !isNaN(num) && isFinite(num)) { return num; } return defaultValue; }; // 센서 데이터 유효성 검증 함수 const validateSensorData = (data: any): boolean => { return data && typeof data === 'object' && typeof data.device_id === 'string' && data.device_id.length > 0; }; const Dashboard: React.FC = () => { const { devices, loading: devicesLoading, error: devicesError, refetch: refetchDevices } = useDevices(); const deviceIds = useMemo(() => devices.map(d => d.device_id), [devices]); console.log('🏠 Dashboard 렌더링 - 디바이스 ID:', deviceIds); const { latestData, loading: dataLoading, error: dataError, refetch: refetchData, isConnected, dataSource, lastUpdate, connectionStatus, retryCount } = useSensorData(deviceIds); console.log('📊 Dashboard 데이터 상태:', { deviceIds, latestDataCount: latestData?.length || 0, loading: dataLoading, error: dataError, isConnected, dataSource, lastUpdate }); // 첫 번째 디바이스 데이터 메모이제이션 - null 체크 강화 const firstDeviceData = useMemo(() => { if (!latestData || !Array.isArray(latestData) || latestData.length === 0) { return null; } const validData = latestData.find(data => validateSensorData(data)); return validData || null; }, [latestData]); // 센서 카드 데이터 메모이제이션 - 새로운 변환 유틸리티 사용 const sensorCards = useMemo((): SensorCardData[] => { return transformSensorDataToCardData(firstDeviceData); }, [firstDeviceData]); // 차트 데이터 메모이제이션 - 새로운 변환 유틸리티 사용 const chartData = useMemo((): ChartDataPoint[] => { try { const transformedData = transformSensorDataToChartData(latestData); // 변환된 데이터 유효성 검증 if (!validateChartData(transformedData)) { console.warn('Invalid chart data detected, using default data'); return transformSensorDataToChartData([]); } return transformedData; } catch (err) { console.error('Error processing chart data:', err); return transformSensorDataToChartData([]); } }, [latestData]); // 에러 처리 const error = devicesError || dataError; if (devicesLoading || dataLoading) { return (
{error}
활성 디바이스
{devices.filter(d => d.status === 'active').length}
총 데이터
{latestData.length}
| 디바이스 ID | 이름 | 상태 | 마지막 연결 |
|---|---|---|---|
| {device.device_id} | {device.name} | {device.status} | {new Date(device.last_seen).toLocaleString()} |