diff options
Diffstat (limited to 'modern/src/other')
-rw-r--r-- | modern/src/other/EventPage.jsx | 108 | ||||
-rw-r--r-- | modern/src/other/GeofencesList.jsx | 54 | ||||
-rw-r--r-- | modern/src/other/GeofencesPage.jsx | 142 | ||||
-rw-r--r-- | modern/src/other/NetworkPage.jsx | 122 | ||||
-rw-r--r-- | modern/src/other/PositionPage.jsx | 110 | ||||
-rw-r--r-- | modern/src/other/ReplayPage.jsx | 233 |
6 files changed, 0 insertions, 769 deletions
diff --git a/modern/src/other/EventPage.jsx b/modern/src/other/EventPage.jsx deleted file mode 100644 index c8d84d5e..00000000 --- a/modern/src/other/EventPage.jsx +++ /dev/null @@ -1,108 +0,0 @@ -import React, { useCallback, useState } from 'react'; - -import { - Typography, AppBar, Toolbar, IconButton, -} from '@mui/material'; -import makeStyles from '@mui/styles/makeStyles'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useEffectAsync } from '../reactHelper'; -import { useTranslation } from '../common/components/LocalizationProvider'; -import MapView from '../map/core/MapView'; -import MapCamera from '../map/MapCamera'; -import MapPositions from '../map/MapPositions'; -import MapGeofence from '../map/MapGeofence'; -import StatusCard from '../common/components/StatusCard'; -import { formatNotificationTitle } from '../common/util/formatter'; - -const useStyles = makeStyles(() => ({ - root: { - height: '100%', - display: 'flex', - flexDirection: 'column', - }, - toolbar: { - zIndex: 1, - }, - mapContainer: { - flexGrow: 1, - }, -})); - -const EventPage = () => { - const classes = useStyles(); - const navigate = useNavigate(); - const t = useTranslation(); - - const { id } = useParams(); - - const [event, setEvent] = useState(); - const [position, setPosition] = useState(); - const [showCard, setShowCard] = useState(false); - - const formatType = (event) => formatNotificationTitle(t, { - type: event.type, - attributes: { - alarms: event.attributes.alarm, - }, - }); - - const onMarkerClick = useCallback((positionId) => { - setShowCard(!!positionId); - }, [setShowCard]); - - useEffectAsync(async () => { - if (id) { - const response = await fetch(`/api/events/${id}`); - if (response.ok) { - setEvent(await response.json()); - } else { - throw Error(await response.text()); - } - } - }, [id]); - - useEffectAsync(async () => { - if (event && event.positionId) { - const response = await fetch(`/api/positions?id=${event.positionId}`); - if (response.ok) { - const positions = await response.json(); - if (positions.length > 0) { - setPosition(positions[0]); - } - } else { - throw Error(await response.text()); - } - } - }, [event]); - - return ( - <div className={classes.root}> - <AppBar color="inherit" position="static" className={classes.toolbar}> - <Toolbar> - <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate('/')}> - <ArrowBackIcon /> - </IconButton> - <Typography variant="h6">{event && formatType(event)}</Typography> - </Toolbar> - </AppBar> - <div className={classes.mapContainer}> - <MapView> - <MapGeofence /> - {position && <MapPositions positions={[position]} onClick={onMarkerClick} titleField="fixTime" />} - </MapView> - {position && <MapCamera latitude={position.latitude} longitude={position.longitude} />} - {position && showCard && ( - <StatusCard - deviceId={position.deviceId} - position={position} - onClose={() => setShowCard(false)} - disableActions - /> - )} - </div> - </div> - ); -}; - -export default EventPage; diff --git a/modern/src/other/GeofencesList.jsx b/modern/src/other/GeofencesList.jsx deleted file mode 100644 index d26eff09..00000000 --- a/modern/src/other/GeofencesList.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, { Fragment } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import makeStyles from '@mui/styles/makeStyles'; -import { - Divider, List, ListItemButton, ListItemText, -} from '@mui/material'; - -import { geofencesActions } from '../store'; -import CollectionActions from '../settings/components/CollectionActions'; -import { useCatchCallback } from '../reactHelper'; - -const useStyles = makeStyles(() => ({ - list: { - maxHeight: '100%', - overflow: 'auto', - }, - icon: { - width: '25px', - height: '25px', - filter: 'brightness(0) invert(1)', - }, -})); - -const GeofencesList = ({ onGeofenceSelected }) => { - const classes = useStyles(); - const dispatch = useDispatch(); - - const items = useSelector((state) => state.geofences.items); - - const refreshGeofences = useCatchCallback(async () => { - const response = await fetch('/api/geofences'); - if (response.ok) { - dispatch(geofencesActions.refresh(await response.json())); - } else { - throw Error(await response.text()); - } - }, [dispatch]); - - return ( - <List className={classes.list}> - {Object.values(items).map((item, index, list) => ( - <Fragment key={item.id}> - <ListItemButton key={item.id} onClick={() => onGeofenceSelected(item.id)}> - <ListItemText primary={item.name} /> - <CollectionActions itemId={item.id} editPath="/settings/geofence" endpoint="geofences" setTimestamp={refreshGeofences} /> - </ListItemButton> - {index < list.length - 1 ? <Divider /> : null} - </Fragment> - ))} - </List> - ); -}; - -export default GeofencesList; diff --git a/modern/src/other/GeofencesPage.jsx b/modern/src/other/GeofencesPage.jsx deleted file mode 100644 index a27a6dca..00000000 --- a/modern/src/other/GeofencesPage.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { useState } from 'react'; -import { useDispatch } from 'react-redux'; -import { - Divider, Typography, IconButton, useMediaQuery, Toolbar, -} from '@mui/material'; -import Tooltip from '@mui/material/Tooltip'; -import makeStyles from '@mui/styles/makeStyles'; -import { useTheme } from '@mui/material/styles'; -import Drawer from '@mui/material/Drawer'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import UploadFileIcon from '@mui/icons-material/UploadFile'; -import { useNavigate } from 'react-router-dom'; -import MapView from '../map/core/MapView'; -import MapCurrentLocation from '../map/MapCurrentLocation'; -import MapGeofenceEdit from '../map/draw/MapGeofenceEdit'; -import GeofencesList from './GeofencesList'; -import { useTranslation } from '../common/components/LocalizationProvider'; -import MapGeocoder from '../map/geocoder/MapGeocoder'; -import { errorsActions } from '../store'; - -const useStyles = makeStyles((theme) => ({ - root: { - height: '100%', - display: 'flex', - flexDirection: 'column', - }, - content: { - flexGrow: 1, - overflow: 'hidden', - display: 'flex', - flexDirection: 'row', - [theme.breakpoints.down('sm')]: { - flexDirection: 'column-reverse', - }, - }, - drawer: { - zIndex: 1, - }, - drawerPaper: { - position: 'relative', - [theme.breakpoints.up('sm')]: { - width: theme.dimensions.drawerWidthTablet, - }, - [theme.breakpoints.down('sm')]: { - height: theme.dimensions.drawerHeightPhone, - }, - }, - mapContainer: { - flexGrow: 1, - }, - title: { - flexGrow: 1, - }, - fileInput: { - display: 'none', - }, -})); - -const GeofencesPage = () => { - const theme = useTheme(); - const classes = useStyles(); - const dispatch = useDispatch(); - const navigate = useNavigate(); - const t = useTranslation(); - - const isPhone = useMediaQuery(theme.breakpoints.down('sm')); - - const [selectedGeofenceId, setSelectedGeofenceId] = useState(); - - const handleFile = (event) => { - const files = Array.from(event.target.files); - const [file] = files; - const reader = new FileReader(); - reader.onload = async () => { - const xml = new DOMParser().parseFromString(reader.result, 'text/xml'); - const segment = xml.getElementsByTagName('trkseg')[0]; - const coordinates = Array.from(segment.getElementsByTagName('trkpt')) - .map((point) => `${point.getAttribute('lat')} ${point.getAttribute('lon')}`) - .join(', '); - const area = `LINESTRING (${coordinates})`; - const newItem = { name: t('sharedGeofence'), area }; - try { - const response = await fetch('/api/geofences', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(newItem), - }); - if (response.ok) { - const item = await response.json(); - navigate(`/settings/geofence/${item.id}`); - } else { - throw Error(await response.text()); - } - } catch (error) { - dispatch(errorsActions.push(error.message)); - } - }; - reader.onerror = (event) => { - dispatch(errorsActions.push(event.target.error)); - }; - reader.readAsText(file); - }; - - return ( - <div className={classes.root}> - <div className={classes.content}> - <Drawer - className={classes.drawer} - anchor={isPhone ? 'bottom' : 'left'} - variant="permanent" - classes={{ paper: classes.drawerPaper }} - > - <Toolbar> - <IconButton edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}> - <ArrowBackIcon /> - </IconButton> - <Typography variant="h6" className={classes.title}>{t('sharedGeofences')}</Typography> - <label htmlFor="upload-gpx"> - <input accept=".gpx" id="upload-gpx" type="file" className={classes.fileInput} onChange={handleFile} /> - <IconButton edge="end" component="span" onClick={() => {}}> - <Tooltip title={t('sharedUpload')}> - <UploadFileIcon /> - </Tooltip> - </IconButton> - </label> - </Toolbar> - <Divider /> - <GeofencesList onGeofenceSelected={setSelectedGeofenceId} /> - </Drawer> - <div className={classes.mapContainer}> - <MapView> - <MapGeofenceEdit selectedGeofenceId={selectedGeofenceId} /> - </MapView> - <MapCurrentLocation /> - <MapGeocoder /> - </div> - </div> - </div> - ); -}; - -export default GeofencesPage; diff --git a/modern/src/other/NetworkPage.jsx b/modern/src/other/NetworkPage.jsx deleted file mode 100644 index 9dc00c61..00000000 --- a/modern/src/other/NetworkPage.jsx +++ /dev/null @@ -1,122 +0,0 @@ -import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; - -import { - Typography, Container, Paper, AppBar, Toolbar, IconButton, Table, TableHead, TableRow, TableCell, TableBody, -} from '@mui/material'; -import makeStyles from '@mui/styles/makeStyles'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useEffectAsync } from '../reactHelper'; - -const useStyles = makeStyles((theme) => ({ - root: { - height: '100%', - display: 'flex', - flexDirection: 'column', - }, - content: { - overflow: 'auto', - paddingTop: theme.spacing(2), - paddingBottom: theme.spacing(2), - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(2), - }, -})); - -const NetworkPage = () => { - const classes = useStyles(); - const navigate = useNavigate(); - - const { positionId } = useParams(); - - const [item, setItem] = useState({}); - - useEffectAsync(async () => { - if (positionId) { - const response = await fetch(`/api/positions?id=${positionId}`); - if (response.ok) { - const positions = await response.json(); - if (positions.length > 0) { - setItem(positions[0]); - } - } else { - throw Error(await response.text()); - } - } - }, [positionId]); - - const deviceName = useSelector((state) => { - if (item) { - const device = state.devices.items[item.deviceId]; - if (device) { - return device.name; - } - } - return null; - }); - - return ( - <div className={classes.root}> - <AppBar position="sticky" color="inherit"> - <Toolbar> - <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}> - <ArrowBackIcon /> - </IconButton> - <Typography variant="h6"> - {deviceName} - </Typography> - </Toolbar> - </AppBar> - <div className={classes.content}> - <Container maxWidth="sm"> - <Paper> - <Table> - <TableHead> - <TableRow> - <TableCell>MCC</TableCell> - <TableCell>MNC</TableCell> - <TableCell>LAC</TableCell> - <TableCell>CID</TableCell> - </TableRow> - </TableHead> - <TableBody> - {(item.network?.cellTowers || []).map((cell) => ( - <TableRow key={cell.cellId}> - <TableCell>{cell.mobileCountryCode}</TableCell> - <TableCell>{cell.mobileNetworkCode}</TableCell> - <TableCell>{cell.locationAreaCode}</TableCell> - <TableCell>{cell.cellId}</TableCell> - </TableRow> - ))} - </TableBody> - </Table> - </Paper> - </Container> - <Container maxWidth="sm"> - <Paper> - <Table> - <TableHead> - <TableRow> - <TableCell>MAC</TableCell> - <TableCell>RSSI</TableCell> - </TableRow> - </TableHead> - <TableBody> - {(item.network?.wifiAccessPoints || []).map((wifi) => ( - <TableRow key={wifi.macAddress}> - <TableCell>{wifi.macAddress}</TableCell> - <TableCell>{wifi.signalStrength}</TableCell> - </TableRow> - ))} - </TableBody> - </Table> - </Paper> - </Container> - </div> - </div> - ); -}; - -export default NetworkPage; diff --git a/modern/src/other/PositionPage.jsx b/modern/src/other/PositionPage.jsx deleted file mode 100644 index f253cd2c..00000000 --- a/modern/src/other/PositionPage.jsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; - -import { - Typography, Container, Paper, AppBar, Toolbar, IconButton, Table, TableHead, TableRow, TableCell, TableBody, -} from '@mui/material'; -import makeStyles from '@mui/styles/makeStyles'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { useNavigate, useParams } from 'react-router-dom'; -import { useEffectAsync } from '../reactHelper'; -import { useTranslation } from '../common/components/LocalizationProvider'; -import PositionValue from '../common/components/PositionValue'; -import usePositionAttributes from '../common/attributes/usePositionAttributes'; - -const useStyles = makeStyles((theme) => ({ - root: { - height: '100%', - display: 'flex', - flexDirection: 'column', - }, - content: { - overflow: 'auto', - paddingTop: theme.spacing(2), - paddingBottom: theme.spacing(2), - }, -})); - -const PositionPage = () => { - const classes = useStyles(); - const navigate = useNavigate(); - const t = useTranslation(); - - const positionAttributes = usePositionAttributes(t); - - const { id } = useParams(); - - const [item, setItem] = useState(); - - useEffectAsync(async () => { - if (id) { - const response = await fetch(`/api/positions?id=${id}`); - if (response.ok) { - const positions = await response.json(); - if (positions.length > 0) { - setItem(positions[0]); - } - } else { - throw Error(await response.text()); - } - } - }, [id]); - - const deviceName = useSelector((state) => { - if (item) { - const device = state.devices.items[item.deviceId]; - if (device) { - return device.name; - } - } - return null; - }); - - return ( - <div className={classes.root}> - <AppBar position="sticky" color="inherit"> - <Toolbar> - <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}> - <ArrowBackIcon /> - </IconButton> - <Typography variant="h6"> - {deviceName} - </Typography> - </Toolbar> - </AppBar> - <div className={classes.content}> - <Container maxWidth="sm"> - <Paper> - <Table> - <TableHead> - <TableRow> - <TableCell>{t('stateName')}</TableCell> - <TableCell>{t('sharedName')}</TableCell> - <TableCell>{t('stateValue')}</TableCell> - </TableRow> - </TableHead> - <TableBody> - {item && Object.getOwnPropertyNames(item).filter((it) => it !== 'attributes').map((property) => ( - <TableRow key={property}> - <TableCell>{property}</TableCell> - <TableCell><strong>{positionAttributes[property]?.name || property}</strong></TableCell> - <TableCell><PositionValue position={item} property={property} /></TableCell> - </TableRow> - ))} - {item && Object.getOwnPropertyNames(item.attributes).map((attribute) => ( - <TableRow key={attribute}> - <TableCell>{attribute}</TableCell> - <TableCell><strong>{positionAttributes[attribute]?.name || attribute}</strong></TableCell> - <TableCell><PositionValue position={item} attribute={attribute} /></TableCell> - </TableRow> - ))} - </TableBody> - </Table> - </Paper> - </Container> - </div> - </div> - ); -}; - -export default PositionPage; diff --git a/modern/src/other/ReplayPage.jsx b/modern/src/other/ReplayPage.jsx deleted file mode 100644 index 1050b976..00000000 --- a/modern/src/other/ReplayPage.jsx +++ /dev/null @@ -1,233 +0,0 @@ -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 { 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'; -import { usePreference } from '../common/util/preferences'; - -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 hours12 = usePreference('twelveHourFormat'); - - 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 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 }) => { - setSelectedDeviceId(deviceId); - setFrom(from); - setTo(to); - const query = new URLSearchParams({ deviceId, from, to }); - 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 { - throw Error(t('sharedNoData')); - } - } else { - throw Error(await response.text()); - } - }); - - const handleDownload = () => { - const query = new URLSearchParams({ deviceId: selectedDeviceId, from, to }); - window.location.assign(`/api/positions/kml?${query.toString()}`); - }; - - 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" /> - )} - </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', hours12)} - </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; |