import dayjs from 'dayjs'; import React, { useState } from 'react'; import { FormControl, InputLabel, Select, MenuItem, } from '@mui/material'; import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts'; import ReportFilter from './components/ReportFilter'; import { formatTime } from '../common/util/formatter'; import { useTranslation } from '../common/components/LocalizationProvider'; import PageLayout from '../common/components/PageLayout'; import ReportsMenu from './components/ReportsMenu'; import usePositionAttributes from '../common/attributes/usePositionAttributes'; import { useCatch } from '../reactHelper'; import { useAttributePreference } from '../common/util/preferences'; import { altitudeFromMeters, distanceFromMeters, speedFromKnots, volumeFromLiters, } from '../common/util/converter'; import useReportStyles from './common/useReportStyles'; const ChartReportPage = () => { const classes = useReportStyles(); const t = useTranslation(); const positionAttributes = usePositionAttributes(t); const distanceUnit = useAttributePreference('distanceUnit'); const altitudeUnit = useAttributePreference('altitudeUnit'); const speedUnit = useAttributePreference('speedUnit'); const volumeUnit = useAttributePreference('volumeUnit'); const [items, setItems] = useState([]); const [types, setTypes] = useState(['speed']); const [type, setType] = useState('speed'); const values = items.map((it) => it[type]); const minValue = Math.min(...values); const maxValue = Math.max(...values); const valueRange = maxValue - minValue; const handleSubmit = useCatch(async ({ deviceId, from, to }) => { const query = new URLSearchParams({ deviceId, from, to }); const response = await fetch(`/api/reports/route?${query.toString()}`, { headers: { Accept: 'application/json' }, }); if (response.ok) { const positions = await response.json(); const keySet = new Set(); const keyList = []; const formattedPositions = positions.map((position) => { const data = { ...position, ...position.attributes }; const formatted = {}; formatted.fixTime = dayjs(position.fixTime).valueOf(); Object.keys(data).filter((key) => !['id', 'deviceId'].includes(key)).forEach((key) => { const value = data[key]; if (typeof value === 'number') { keySet.add(key); const definition = positionAttributes[key] || {}; switch (definition.dataType) { case 'speed': formatted[key] = speedFromKnots(value, speedUnit).toFixed(2); break; case 'altitude': formatted[key] = altitudeFromMeters(value, altitudeUnit).toFixed(2); break; case 'distance': formatted[key] = distanceFromMeters(value, distanceUnit).toFixed(2); break; case 'volume': formatted[key] = volumeFromLiters(value, volumeUnit).toFixed(2); break; case 'hours': formatted[key] = (value / 1000).toFixed(2); break; default: formatted[key] = value; break; } } }); return formatted; }); Object.keys(positionAttributes).forEach((key) => { if (keySet.has(key)) { keyList.push(key); keySet.delete(key); } }); setTypes([...keyList, ...keySet]); setItems(formattedPositions); } else { throw Error(await response.text()); } }); return ( } breadcrumbs={['reportTitle', 'reportChart']}>
{t('reportChartType')}
{items.length > 0 && (
formatTime(value, 'time')} domain={['dataMin', 'dataMax']} scale="time" /> value.toFixed(2)} domain={[minValue - valueRange / 5, maxValue + valueRange / 5]} /> [value, positionAttributes[key]?.name || key]} labelFormatter={(value) => formatTime(value, 'seconds')} />
)}
); }; export default ChartReportPage;