aboutsummaryrefslogtreecommitdiff
path: root/modern/src/main
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-10-16 16:50:37 -0700
committerAnton Tananaev <anton@traccar.org>2022-10-16 16:50:37 -0700
commit16600db340dbdaa7f4b941e8e1bd6c6282d6a557 (patch)
treec09a285e3ff33dd16ae5b22380161c4763619d10 /modern/src/main
parentac8adb34066745322297ae5a63feb00724d503a6 (diff)
downloadtrackermap-web-16600db340dbdaa7f4b941e8e1bd6c6282d6a557.tar.gz
trackermap-web-16600db340dbdaa7f4b941e8e1bd6c6282d6a557.tar.bz2
trackermap-web-16600db340dbdaa7f4b941e8e1bd6c6282d6a557.zip
Move status card
Diffstat (limited to 'modern/src/main')
-rw-r--r--modern/src/main/MainPage.js3
-rw-r--r--modern/src/main/StatusCard.js267
2 files changed, 2 insertions, 268 deletions
diff --git a/modern/src/main/MainPage.js b/modern/src/main/MainPage.js
index 90e1b920..a174096e 100644
--- a/modern/src/main/MainPage.js
+++ b/modern/src/main/MainPage.js
@@ -25,7 +25,7 @@ import BottomMenu from '../common/components/BottomMenu';
import { useTranslation } from '../common/components/LocalizationProvider';
import PoiMap from '../map/main/PoiMap';
import MapPadding from '../map/MapPadding';
-import StatusCard from './StatusCard';
+import StatusCard from '../common/components/StatusCard';
import { devicesActions } from '../store';
import MapDefaultCamera from '../map/main/MapDefaultCamera';
import usePersistedState from '../common/util/usePersistedState';
@@ -370,6 +370,7 @@ const MainPage = () => {
<div className={classes.statusCard}>
<StatusCard
deviceId={selectedDeviceId}
+ position={positions[selectedDeviceId]}
onClose={() => dispatch(devicesActions.select(null))}
/>
</div>
diff --git a/modern/src/main/StatusCard.js b/modern/src/main/StatusCard.js
deleted file mode 100644
index 5ca45066..00000000
--- a/modern/src/main/StatusCard.js
+++ /dev/null
@@ -1,267 +0,0 @@
-import React, { useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
-import { useNavigate } from 'react-router-dom';
-import Draggable from 'react-draggable';
-import {
- Card,
- CardContent,
- Typography,
- CardActions,
- IconButton,
- Table,
- TableBody,
- TableRow,
- TableCell,
- Menu,
- MenuItem,
- CardMedia,
-} from '@mui/material';
-import makeStyles from '@mui/styles/makeStyles';
-import CloseIcon from '@mui/icons-material/Close';
-import ReplayIcon from '@mui/icons-material/Replay';
-import PublishIcon from '@mui/icons-material/Publish';
-import EditIcon from '@mui/icons-material/Edit';
-import DeleteIcon from '@mui/icons-material/Delete';
-import PendingIcon from '@mui/icons-material/Pending';
-
-import { useTranslation } from '../common/components/LocalizationProvider';
-import RemoveDialog from '../common/components/RemoveDialog';
-import PositionValue from '../common/components/PositionValue';
-import { useDeviceReadonly, useRestriction } from '../common/util/permissions';
-import usePersistedState from '../common/util/usePersistedState';
-import usePositionAttributes from '../common/attributes/usePositionAttributes';
-import { devicesActions } from '../store';
-import { useCatch, useCatchCallback } from '../reactHelper';
-
-const useStyles = makeStyles((theme) => ({
- card: {
- width: theme.dimensions.popupMaxWidth,
- },
- media: {
- height: theme.dimensions.popupImageHeight,
- display: 'flex',
- justifyContent: 'flex-end',
- alignItems: 'flex-start',
- },
- mediaButton: {
- color: theme.palette.colors.white,
- mixBlendMode: 'difference',
- },
- header: {
- display: 'flex',
- justifyContent: 'space-between',
- alignItems: 'center',
- padding: theme.spacing(1, 1, 0, 2),
- },
- content: {
- paddingTop: theme.spacing(1),
- paddingBottom: theme.spacing(1),
- },
- negative: {
- color: theme.palette.colors.negative,
- },
- icon: {
- width: '25px',
- height: '25px',
- filter: 'brightness(0) invert(1)',
- },
- table: {
- '& .MuiTableCell-sizeSmall': {
- paddingLeft: 0,
- paddingRight: 0,
- },
- },
- cell: {
- borderBottom: 'none',
- },
- actions: {
- justifyContent: 'space-between',
- },
-}));
-
-const StatusRow = ({ name, content }) => {
- const classes = useStyles();
-
- return (
- <TableRow>
- <TableCell className={classes.cell}>
- <Typography variant="body2">{name}</Typography>
- </TableCell>
- <TableCell className={classes.cell}>
- <Typography variant="body2" color="textSecondary">{content}</Typography>
- </TableCell>
- </TableRow>
- );
-};
-
-const StatusCard = ({ deviceId, onClose }) => {
- const classes = useStyles();
- const navigate = useNavigate();
- const dispatch = useDispatch();
- const t = useTranslation();
-
- const readonly = useRestriction('readonly');
- const deviceReadonly = useDeviceReadonly();
-
- const device = useSelector((state) => state.devices.items[deviceId]);
- const position = useSelector((state) => state.positions.items[deviceId]);
-
- const deviceImage = device?.attributes?.deviceImage;
-
- const positionAttributes = usePositionAttributes(t);
- const [positionItems] = usePersistedState('positionItems', ['speed', 'address', 'totalDistance', 'course']);
-
- const [anchorEl, setAnchorEl] = useState(null);
-
- const [removing, setRemoving] = useState(false);
-
- const handleRemove = useCatch(async (removed) => {
- if (removed) {
- const response = await fetch('/api/devices');
- if (response.ok) {
- dispatch(devicesActions.refresh(await response.json()));
- } else {
- throw Error(await response.text());
- }
- }
- setRemoving(false);
- });
-
- const handleGeofence = useCatchCallback(async () => {
- const newItem = {
- name: '',
- area: `CIRCLE (${position.latitude} ${position.longitude}, 50)`,
- };
- 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();
- const permissionResponse = await fetch('/api/permissions', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ deviceId: position.deviceId, geofenceId: item.id }),
- });
- if (!permissionResponse.ok) {
- throw Error(await permissionResponse.text());
- }
- navigate(`/settings/geofence/${item.id}`);
- } else {
- throw Error(await response.text());
- }
- }, [navigate]);
-
- return (
- <>
- {device && (
- <Draggable
- handle={`.${classes.media}, .${classes.header}`}
- >
- <Card elevation={3} className={classes.card}>
- {deviceImage ? (
- <CardMedia
- className={classes.media}
- image={`/api/media/${device.uniqueId}/${deviceImage}`}
- >
- <IconButton
- size="small"
- onClick={onClose}
- onTouchStart={onClose}
- >
- <CloseIcon fontSize="small" className={classes.mediaButton} />
- </IconButton>
- </CardMedia>
- ) : (
- <div className={classes.header}>
- <Typography variant="body2" color="textSecondary">
- {device.name}
- </Typography>
- <IconButton
- size="small"
- onClick={onClose}
- onTouchStart={onClose}
- >
- <CloseIcon fontSize="small" />
- </IconButton>
- </div>
- )}
- {position && (
- <CardContent className={classes.content}>
- <Table size="small" classes={{ root: classes.table }}>
- <TableBody>
- {positionItems.filter((key) => position.hasOwnProperty(key) || position.attributes.hasOwnProperty(key)).map((key) => (
- <StatusRow
- key={key}
- name={positionAttributes[key].name}
- content={(
- <PositionValue
- position={position}
- property={position.hasOwnProperty(key) ? key : null}
- attribute={position.hasOwnProperty(key) ? null : key}
- />
- )}
- />
- ))}
- </TableBody>
- </Table>
- </CardContent>
- )}
- <CardActions classes={{ root: classes.actions }} disableSpacing>
- <IconButton
- color="secondary"
- onClick={(e) => setAnchorEl(e.currentTarget)}
- disabled={!position}
- >
- <PendingIcon />
- </IconButton>
- <IconButton
- onClick={() => navigate('/replay')}
- disabled={!position}
- >
- <ReplayIcon />
- </IconButton>
- <IconButton
- onClick={() => navigate(`/settings/command-send/${deviceId}`)}
- disabled={readonly}
- >
- <PublishIcon />
- </IconButton>
- <IconButton
- onClick={() => navigate(`/settings/device/${deviceId}`)}
- disabled={deviceReadonly}
- >
- <EditIcon />
- </IconButton>
- <IconButton
- onClick={() => setRemoving(true)}
- disabled={deviceReadonly}
- className={classes.negative}
- >
- <DeleteIcon />
- </IconButton>
- </CardActions>
- </Card>
- </Draggable>
- )}
- {position && (
- <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)}>
- <MenuItem onClick={() => navigate(`/position/${position.id}`)}><Typography color="secondary">{t('sharedShowDetails')}</Typography></MenuItem>
- <MenuItem onClick={handleGeofence}>{t('sharedCreateGeofence')}</MenuItem>
- <MenuItem component="a" target="_blank" href={`https://www.google.com/maps/search/?api=1&query=${position.latitude}%2C${position.longitude}`}>{t('linkGoogleMaps')}</MenuItem>
- <MenuItem component="a" target="_blank" href={`http://maps.apple.com/?ll=${position.latitude},${position.longitude}`}>{t('linkAppleMaps')}</MenuItem>
- <MenuItem component="a" target="_blank" href={`https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${position.latitude}%2C${position.longitude}&heading=${position.course}`}>{t('linkStreetView')}</MenuItem>
- </Menu>
- )}
- <RemoveDialog
- open={removing}
- endpoint="devices"
- itemId={deviceId}
- onResult={(removed) => handleRemove(removed)}
- />
- </>
- );
-};
-
-export default StatusCard;