optimization
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s
This commit is contained in:
@@ -1,104 +1,106 @@
|
||||
import { useState, useMemo, memo, useCallback } from 'react'
|
||||
import { BarChart3 } from 'lucide-react'
|
||||
import { LineChart, Panel } from '@/components/Charts'
|
||||
import { TimeRangeSelector } from './TimeRangeSelector'
|
||||
import { DataGap } from './utils'
|
||||
import { DataGap, detectDataGaps, normalizeTelemetryData } from '@/features/daily-report/utils'
|
||||
import { TelemetryDto } from '@/lib/api'
|
||||
import { EmptyState } from '@/components/common'
|
||||
import { useTelemetryCharts } from '@/features/daily-report/hooks/useTelemetryCharts'
|
||||
|
||||
type ChartsTabProps = {
|
||||
chartStartMinute: number
|
||||
chartEndMinute: number
|
||||
onStartMinuteChange: (minute: number) => void
|
||||
onEndMinuteChange: (minute: number) => void
|
||||
labels: string[]
|
||||
soil: (number | null)[]
|
||||
humidity: (number | null)[]
|
||||
temperature: (number | null)[]
|
||||
lux: (number | null)[]
|
||||
gas: (number | null)[]
|
||||
tempMinMax: { min: number; max: number }
|
||||
luxMinMax: { min: number; max: number }
|
||||
totalRecords: number
|
||||
sortedTelemetry: TelemetryDto[]
|
||||
dataGaps?: DataGap[]
|
||||
}
|
||||
|
||||
export function ChartsTab({
|
||||
chartStartMinute,
|
||||
chartEndMinute,
|
||||
onStartMinuteChange,
|
||||
onEndMinuteChange,
|
||||
labels,
|
||||
soil,
|
||||
humidity,
|
||||
temperature,
|
||||
lux,
|
||||
gas,
|
||||
tempMinMax,
|
||||
luxMinMax,
|
||||
totalRecords,
|
||||
dataGaps = []
|
||||
export const ChartsTab = memo(function ChartsTab({
|
||||
sortedTelemetry,
|
||||
dataGaps = [],
|
||||
}: ChartsTabProps) {
|
||||
const [chartStartMinute, setChartStartMinute] = useState(0)
|
||||
const [chartEndMinute, setChartEndMinute] = useState(1439)
|
||||
|
||||
const handleStartMinuteChange = useCallback((minute: number) => {
|
||||
setChartStartMinute(minute)
|
||||
}, [])
|
||||
|
||||
const handleEndMinuteChange = useCallback((minute: number) => {
|
||||
setChartEndMinute(minute)
|
||||
}, [])
|
||||
|
||||
// Normalize telemetry data
|
||||
const normalizedTelemetry = useMemo(
|
||||
() => normalizeTelemetryData(sortedTelemetry),
|
||||
[sortedTelemetry]
|
||||
)
|
||||
|
||||
// Filter by time range
|
||||
const filteredTelemetry = useMemo(() => {
|
||||
return normalizedTelemetry.filter(
|
||||
t => t.minute >= chartStartMinute && t.minute <= chartEndMinute
|
||||
)
|
||||
}, [normalizedTelemetry, chartStartMinute, chartEndMinute])
|
||||
|
||||
// Detect data gaps in filtered data
|
||||
const timestamps = useMemo(
|
||||
() => filteredTelemetry.map(t => t.timestamp),
|
||||
[filteredTelemetry]
|
||||
)
|
||||
|
||||
const filteredDataGaps = useMemo(
|
||||
() => detectDataGaps(timestamps, 30),
|
||||
[timestamps]
|
||||
)
|
||||
|
||||
// Build charts using custom hook
|
||||
const { charts, chartLabels } = useTelemetryCharts({
|
||||
filteredTelemetry,
|
||||
filteredDataGaps,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Time Range Selector */}
|
||||
<TimeRangeSelector
|
||||
startMinute={chartStartMinute}
|
||||
endMinute={chartEndMinute}
|
||||
onStartMinuteChange={onStartMinuteChange}
|
||||
onEndMinuteChange={onEndMinuteChange}
|
||||
totalRecords={totalRecords}
|
||||
onStartMinuteChange={handleStartMinuteChange}
|
||||
onEndMinuteChange={handleEndMinuteChange}
|
||||
totalRecords={filteredTelemetry.length}
|
||||
dataGaps={dataGaps}
|
||||
/>
|
||||
|
||||
{/* Charts Grid */}
|
||||
{totalRecords === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-gray-500">
|
||||
<BarChart3 className="w-12 h-12 text-gray-300 mb-4" />
|
||||
<p className="text-gray-600">دادهای برای این بازه زمانی موجود نیست</p>
|
||||
</div>
|
||||
{filteredTelemetry.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={BarChart3}
|
||||
title="دادهای موجود نیست"
|
||||
message="دادهای برای این بازه زمانی موجود نیست"
|
||||
/>
|
||||
) : (
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<Panel title="رطوبت خاک">
|
||||
<LineChart
|
||||
labels={labels}
|
||||
series={[{ label: 'رطوبت خاک (%)', data: soil as (number | null)[], borderColor: '#16a34a', backgroundColor: '#dcfce7', fill: true }]}
|
||||
yAxisMin={0}
|
||||
yAxisMax={100}
|
||||
/>
|
||||
</Panel>
|
||||
<Panel title="رطوبت">
|
||||
<LineChart
|
||||
labels={labels}
|
||||
series={[{ label: 'رطوبت (%)', data: humidity as (number | null)[], borderColor: '#3b82f6', backgroundColor: '#dbeafe', fill: true }]}
|
||||
yAxisMin={0}
|
||||
yAxisMax={100}
|
||||
/>
|
||||
</Panel>
|
||||
<Panel title="دما">
|
||||
<LineChart
|
||||
labels={labels}
|
||||
series={[{ label: 'دما (°C)', data: temperature as (number | null)[], borderColor: '#ef4444', backgroundColor: '#fee2e2', fill: true }]}
|
||||
yAxisMin={tempMinMax.min}
|
||||
yAxisMax={tempMinMax.max}
|
||||
/>
|
||||
</Panel>
|
||||
<Panel title="نور">
|
||||
<LineChart
|
||||
labels={labels}
|
||||
series={[{ label: 'Lux', data: lux as (number | null)[], borderColor: '#a855f7', backgroundColor: '#f3e8ff', fill: true }]}
|
||||
yAxisMin={luxMinMax.min}
|
||||
yAxisMax={luxMinMax.max}
|
||||
/>
|
||||
</Panel>
|
||||
<Panel title="گاز CO">
|
||||
<LineChart
|
||||
labels={labels}
|
||||
series={[{ label: 'CO (ppm)', data: gas as (number | null)[], borderColor: '#f59e0b', backgroundColor: '#fef3c7', fill: true }]}
|
||||
yAxisMin={0}
|
||||
yAxisMax={100}
|
||||
/>
|
||||
</Panel>
|
||||
{charts.map(chart => (
|
||||
<Panel key={chart.key} title={chart.title}>
|
||||
<LineChart
|
||||
labels={chartLabels}
|
||||
series={[
|
||||
{
|
||||
label: chart.seriesLabel,
|
||||
data: chart.data,
|
||||
borderColor: chart.color,
|
||||
backgroundColor: chart.bgColor,
|
||||
fill: true,
|
||||
},
|
||||
]}
|
||||
yAxisMin={chart.yAxisMin}
|
||||
yAxisMax={chart.yAxisMax}
|
||||
/>
|
||||
</Panel>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}, (prevProps, nextProps) => {
|
||||
// Custom comparison for better performance
|
||||
return prevProps.sortedTelemetry.length === nextProps.sortedTelemetry.length &&
|
||||
(prevProps.dataGaps?.length ?? 0) === (nextProps.dataGaps?.length ?? 0) &&
|
||||
prevProps.sortedTelemetry[0]?.id === nextProps.sortedTelemetry[0]?.id
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user