| New file |
| | |
| | | import React, { useState, useEffect, useMemo } from 'react'; |
| | | import { useTranslate } from "react-admin"; |
| | | import { |
| | | Box, |
| | | Tabs, |
| | | Tab, |
| | | Divider, |
| | | List, |
| | | ListItemButton, |
| | | ListItemText, |
| | | Typography, |
| | | Stack, |
| | | } from '@mui/material'; |
| | | import JsonShow from '../../JsonShow'; |
| | | import { fetchAreaList } from '../../http'; |
| | | |
| | | const AreaInsight = ({ curZone, setTitle }) => { |
| | | const translate = useTranslate(); |
| | | const [areas, setAreas] = useState([]); |
| | | const [selectedId, setSelectedId] = useState(null); |
| | | const [activeTab, setActiveTab] = useState(0); |
| | | const formatCoord = (value) => { |
| | | if (value === null || value === undefined) { |
| | | return '-'; |
| | | } |
| | | if (typeof value === 'number') { |
| | | return value.toFixed(2); |
| | | } |
| | | const num = Number(value); |
| | | if (!Number.isNaN(num)) { |
| | | return num.toFixed(2); |
| | | } |
| | | return String(value); |
| | | }; |
| | | |
| | | useEffect(() => { |
| | | let mounted = true; |
| | | if (!curZone) { |
| | | setAreas([]); |
| | | setSelectedId(null); |
| | | return; |
| | | } |
| | | fetchAreaList(curZone).then((list) => { |
| | | if (!mounted) { |
| | | return; |
| | | } |
| | | const nextList = Array.isArray(list) ? list : []; |
| | | setAreas(nextList); |
| | | setSelectedId((prev) => { |
| | | if (prev) { |
| | | const exists = nextList.some((item) => item?.id === prev); |
| | | if (exists) { |
| | | return prev; |
| | | } |
| | | } |
| | | return nextList[0]?.id ?? null; |
| | | }); |
| | | }); |
| | | return () => { |
| | | mounted = false; |
| | | }; |
| | | }, [curZone]); |
| | | |
| | | const selectedArea = useMemo( |
| | | () => areas.find((area) => area?.id === selectedId) || null, |
| | | [areas, selectedId] |
| | | ); |
| | | |
| | | useEffect(() => { |
| | | if (setTitle) { |
| | | const label = selectedArea |
| | | ? `${translate('page.map.devices.area')} - ${selectedArea.name || selectedArea.id}` |
| | | : translate('page.map.devices.area'); |
| | | setTitle(label); |
| | | } |
| | | }, [selectedArea, setTitle, translate]); |
| | | |
| | | useEffect(() => { |
| | | return () => { |
| | | if (setTitle) { |
| | | setTitle(null); |
| | | } |
| | | }; |
| | | }, [setTitle]); |
| | | |
| | | const handleTabChange = (_event, newValue) => { |
| | | setActiveTab(newValue); |
| | | }; |
| | | |
| | | const detailPairs = [ |
| | | { label: translate('page.map.area.form.name'), value: selectedArea?.name }, |
| | | { label: translate('page.map.area.form.code'), value: selectedArea?.code }, |
| | | { label: translate('page.map.area.form.maxQty'), value: selectedArea?.maxCount }, |
| | | { label: translate('page.map.area.form.speedLimit'), value: selectedArea?.speedLimit }, |
| | | { label: translate('page.map.area.form.priority'), value: selectedArea?.priority }, |
| | | ]; |
| | | |
| | | return ( |
| | | <Box sx={{ height: '100%', display: 'flex', gap: 2 }}> |
| | | <Box width={220} sx={{ borderRight: 1, borderColor: 'divider', overflowY: 'auto' }}> |
| | | <List dense disablePadding> |
| | | {areas.map((area) => ( |
| | | <ListItemButton |
| | | key={area.id} |
| | | selected={area.id === selectedId} |
| | | onClick={() => setSelectedId(area.id)} |
| | | > |
| | | <ListItemText |
| | | primary={area.name || `#${area.id}`} |
| | | secondary={area.code} |
| | | /> |
| | | </ListItemButton> |
| | | ))} |
| | | {areas.length === 0 && ( |
| | | <Typography variant="body2" color="text.secondary" p={2}> |
| | | {translate('page.map.area.form.codesEmpty')} |
| | | </Typography> |
| | | )} |
| | | </List> |
| | | </Box> |
| | | |
| | | <Box sx={{ flex: 1, display: 'flex', flexDirection: 'column' }}> |
| | | <Tabs |
| | | value={activeTab} |
| | | onChange={handleTabChange} |
| | | sx={{ mb: 0 }} |
| | | > |
| | | <Tab label={translate('page.map.insight.title')} /> |
| | | <Tab label="JSON" /> |
| | | </Tabs> |
| | | |
| | | <Divider /> |
| | | |
| | | <Box flex={1} pt={2}> |
| | | {activeTab === 0 && ( |
| | | <Stack spacing={1}> |
| | | {detailPairs.map(({ label, value }) => ( |
| | | <Stack key={label} direction="row" spacing={1}> |
| | | <Typography variant="body2" color="text.secondary" minWidth={120}> |
| | | {label}: |
| | | </Typography> |
| | | <Typography variant="body2"> |
| | | {value != null && value !== '' ? value : '-'} |
| | | </Typography> |
| | | </Stack> |
| | | ))} |
| | | {selectedArea?.start && selectedArea?.end && ( |
| | | <> |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('page.map.area.form.startX')}/{translate('page.map.area.form.startY')}: |
| | | </Typography> |
| | | <Typography variant="body2"> |
| | | {`${formatCoord(selectedArea.start.x)} , ${formatCoord(selectedArea.start.y)}`} |
| | | </Typography> |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('page.map.area.form.endX')}/{translate('page.map.area.form.endY')}: |
| | | </Typography> |
| | | <Typography variant="body2"> |
| | | {`${formatCoord(selectedArea.end.x)} , ${formatCoord(selectedArea.end.y)}`} |
| | | </Typography> |
| | | </> |
| | | )} |
| | | {(selectedArea?.codeList?.length ?? 0) > 0 && ( |
| | | <Typography variant="body2"> |
| | | {translate('page.map.area.form.codes', { count: selectedArea.codeList.length })} |
| | | </Typography> |
| | | )} |
| | | </Stack> |
| | | )} |
| | | {activeTab === 1 && ( |
| | | <JsonShow |
| | | data={selectedArea || {}} |
| | | height={550} |
| | | /> |
| | | )} |
| | | </Box> |
| | | </Box> |
| | | </Box> |
| | | ); |
| | | }; |
| | | |
| | | export default AreaInsight; |