Comments (4)
You should send this as a pull request.
from traccar-web.
Its not working yet, so I didnt sent a pull request. Im here looking for help
from traccar-web.
@tananaev Can you reopen till myself or someone figures a solution to this?
from traccar-web.
I ended up modifying ReplayPage.jsx using code from what is already integrated in traccar web. It is not the fastest and takes some time to show the markers but it works well for my application. For anyone looking for a quick help here is the code :
import React, {
useState, useEffect, useRef, useCallback,
} from 'react';
import {
IconButton, Paper, Slider, Toolbar, Typography,
} from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import TuneIcon from '@mui/icons-material/Tune';
import DownloadIcon from '@mui/icons-material/Download';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import PauseIcon from '@mui/icons-material/Pause';
import FastForwardIcon from '@mui/icons-material/FastForward';
import FastRewindIcon from '@mui/icons-material/FastRewind';
import { useNavigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import MapView from '../map/core/MapView';
import MapRoutePath from '../map/MapRoutePath';
import MapRoutePoints from '../map/MapRoutePoints';
import MapPositions from '../map/MapPositions';
import MapMarkers from '../map/MapMarkers';
import { formatTime } from '../common/util/formatter';
import ReportFilter from '../reports/components/ReportFilter';
import { useTranslation } from '../common/components/LocalizationProvider';
import { useCatch } from '../reactHelper';
import MapCamera from '../map/MapCamera';
import MapGeofence from '../map/MapGeofence';
import StatusCard from '../common/components/StatusCard';
const useStyles = makeStyles((theme) => ({
root: {
height: '100%',
},
sidebar: {
display: 'flex',
flexDirection: 'column',
position: 'fixed',
zIndex: 3,
left: 0,
top: 0,
margin: theme.spacing(1.5),
width: theme.dimensions.drawerWidthDesktop,
[theme.breakpoints.down('md')]: {
width: '100%',
margin: 0,
},
},
title: {
flexGrow: 1,
},
slider: {
width: '100%',
},
controls: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
formControlLabel: {
height: '100%',
width: '100%',
paddingRight: theme.spacing(1),
justifyContent: 'space-between',
alignItems: 'center',
},
content: {
display: 'flex',
flexDirection: 'column',
padding: theme.spacing(2),
[theme.breakpoints.down('md')]: {
margin: theme.spacing(1),
},
[theme.breakpoints.up('md')]: {
marginTop: theme.spacing(1),
},
},
}));
const ReplayPage = () => {
const t = useTranslation();
const classes = useStyles();
const navigate = useNavigate();
const timerRef = useRef();
const defaultDeviceId = useSelector((state) => state.devices.selectedId);
const [positions, setPositions] = useState([]);
const [index, setIndex] = useState(0);
const [selectedDeviceId, setSelectedDeviceId] = useState(defaultDeviceId);
const [showCard, setShowCard] = useState(false);
const [from, setFrom] = useState();
const [to, setTo] = useState();
const [expanded, setExpanded] = useState(true);
const [playing, setPlaying] = useState(false);
const [stops, setStops] = useState([]);
const deviceName = useSelector((state) => {
if (selectedDeviceId) {
const device = state.devices.items[selectedDeviceId];
if (device) {
return device.name;
}
}
return null;
});
useEffect(() => {
if (playing && positions.length > 0) {
timerRef.current = setInterval(() => {
setIndex((index) => index + 1);
}, 500);
} else {
clearInterval(timerRef.current);
}
return () => clearInterval(timerRef.current);
}, [playing, positions]);
useEffect(() => {
if (index >= positions.length - 1) {
clearInterval(timerRef.current);
setPlaying(false);
}
}, [index, positions]);
const onPointClick = useCallback((_, index) => {
setIndex(index);
}, [setIndex]);
const onMarkerClick = useCallback((positionId) => {
setShowCard(!!positionId);
}, [setShowCard]);
const handleSubmit = useCatch(async ({ deviceId, from, to }) => {
console.log('Submitting report with:', { deviceId, from, to });
setSelectedDeviceId(deviceId);
setFrom(from);
setTo(to);
const query = new URLSearchParams({ deviceId, from, to });
console.log('Fetching positions with query:', query.toString());
try {
const response = await fetch(`/api/positions?${query.toString()}`);
if (response.ok) {
setIndex(0);
const positions = await response.json();
setPositions(positions);
if (positions.length) {
setExpanded(false);
} else {
console.error('No data available');
}
} else {
const error = await response.text();
console.error('Error fetching positions:', error);
}
console.log('Fetching stops with query:', query.toString());
const stopsResponse = await fetch(`/api/reports/stops?${query.toString()}`, {
headers: { Accept: 'application/json' },
});
if (stopsResponse.ok) {
const stops = await stopsResponse.json();
setStops(stops);
} else {
const error = await stopsResponse.text();
console.error('Error fetching stops:', error);
}
} catch (error) {
console.error('An unexpected error occurred:', error);
}
});
const handleDownload = () => {
const query = new URLSearchParams({ deviceId: selectedDeviceId, from, to });
window.location.assign(`/api/positions/kml?${query.toString()}`);
};
const createMarkers = () => {
if (stops.length === 0) {
console.log('No stops to create markers from.');
return [];
}
console.log('Creating markers from stops:', stops);
return stops.map(stop => ({
latitude: stop.latitude,
longitude: stop.longitude,
title: formatTime(stop.startTime, 'minutes'),
image: 'default-neutral',
}));
};
return (
<div className={classes.root}>
<MapView>
<MapGeofence />
<MapRoutePath positions={positions} />
<MapRoutePoints positions={positions} onClick={onPointClick} />
{index < positions.length && (
<MapPositions positions={[positions[index]]} onClick={onMarkerClick} titleField="fixTime" />
)}
<MapMarkers markers={createMarkers()} showTitles />
</MapView>
<MapCamera positions={positions} />
<div className={classes.sidebar}>
<Paper elevation={3} square>
<Toolbar>
<IconButton edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>{t('reportReplay')}</Typography>
{!expanded && (
<>
<IconButton onClick={handleDownload}>
<DownloadIcon />
</IconButton>
<IconButton edge="end" onClick={() => setExpanded(true)}>
<TuneIcon />
</IconButton>
</>
)}
</Toolbar>
</Paper>
<Paper className={classes.content} square>
{!expanded ? (
<>
<Typography variant="subtitle1" align="center">{deviceName}</Typography>
<Slider
className={classes.slider}
max={positions.length - 1}
step={null}
marks={positions.map((_, index) => ({ value: index }))}
value={index}
onChange={(_, index) => setIndex(index)}
/>
<div className={classes.controls}>
{`${index + 1}/${positions.length}`}
<IconButton onClick={() => setIndex((index) => index - 1)} disabled={playing || index <= 0}>
<FastRewindIcon />
</IconButton>
<IconButton onClick={() => setPlaying(!playing)} disabled={index >= positions.length - 1}>
{playing ? <PauseIcon /> : <PlayArrowIcon />}
</IconButton>
<IconButton onClick={() => setIndex((index) => index + 1)} disabled={playing || index >= positions.length - 1}>
<FastForwardIcon />
</IconButton>
{formatTime(positions[index].fixTime, 'seconds')}
</div>
</>
) : (
<ReportFilter handleSubmit={handleSubmit} fullScreen showOnly />
)}
</Paper>
</div>
{showCard && index < positions.length && (
<StatusCard
deviceId={selectedDeviceId}
position={positions[index]}
onClose={() => setShowCard(false)}
disableActions
/>
)}
</div>
);
};
export default ReplayPage;
from traccar-web.
Related Issues (20)
- Yandex browser problem HOT 4
- Translation error HOT 1
- Automatic Selection In Device List When Vehicle Icon is selected on the map.
- Limit number of markers on the map HOT 7
- [Feature request] Reports duration show in hours and minutes HOT 3
- Does Traccar support the new Coban 405a with a temperature sensor? HOT 1
- Free form notes, per device, for read only users. HOT 2
- Showing other device positions when displaying report(s) on a map HOT 6
- Lov2 HOT 1
- Regarding Traccar-Web Running issues in windows PC HOT 1
- Feature request - Who is it assigned to?
- Cannot see a "Status" of a device HOT 10
- Feature Request: Reports use attribute "Web: Report Color" to display lines HOT 4
- Add more info in calendar HOT 1
- Adjust device popup positioning
- Show version number on the initial login page HOT 4
- Error after comment #1243 HOT 4
- driver identification by qrcode HOT 1
- OpenID issue with push notifications
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from traccar-web.