diff --git a/package-lock.json b/package-lock.json index 54072e5..4a8e2bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ucentral-client", - "version": "2.10.0(20)", + "version": "2.10.0(21)", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ucentral-client", - "version": "2.10.0(20)", + "version": "2.10.0(21)", "license": "ISC", "dependencies": { "@chakra-ui/icons": "^2.0.18", diff --git a/package.json b/package.json index d3c6c58..19af39f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ucentral-client", - "version": "2.10.0(20)", + "version": "2.10.0(21)", "description": "", "private": true, "main": "index.tsx", diff --git a/src/@tanstack.react-table.d.ts b/src/@tanstack.react-table.d.ts index fc6659d..826908f 100644 --- a/src/@tanstack.react-table.d.ts +++ b/src/@tanstack.react-table.d.ts @@ -6,6 +6,7 @@ declare module '@tanstack/table-core' { interface ColumnMeta { stopPropagation?: boolean; alwaysShow?: boolean; + anchored?: boolean; hasPopover?: boolean; customMaxWidth?: string; customMinWidth?: string; diff --git a/src/components/Containers/ResponsiveTag/index.tsx b/src/components/Containers/ResponsiveTag/index.tsx new file mode 100644 index 0000000..7508cbb --- /dev/null +++ b/src/components/Containers/ResponsiveTag/index.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { As, Icon, Tag, TagLabel, TagLeftIcon, TagProps, Tooltip, useBreakpoint } from '@chakra-ui/react'; + +export interface ResponsiveTagProps extends TagProps { + label: string; + icon: As; + tooltip?: string; + isCompact?: boolean; +} + +export const ResponsiveTag = React.memo(({ label, icon, tooltip, isCompact, ...props }: ResponsiveTagProps) => { + const breakpoint = useBreakpoint(); + + const isCompactVersion = isCompact || breakpoint === 'base' || breakpoint === 'sm'; + + if (isCompactVersion) { + return ( + + + + + + ); + } + + return ( + + + + {label} + + + ); +}); diff --git a/src/hooks/Network/Devices.ts b/src/hooks/Network/Devices.ts index 0f8c85c..3ebac10 100644 --- a/src/hooks/Network/Devices.ts +++ b/src/hooks/Network/Devices.ts @@ -58,6 +58,7 @@ export type DeviceWithStatus = { lastConfigurationDownload: number; lastContact: number | string; lastFWUpdate: number; + lastRecordedContact: number; load: number; locale: string; location: string; @@ -73,6 +74,7 @@ export type DeviceWithStatus = { restrictedDevice: boolean; rxBytes: number; serialNumber: string; + simulated: boolean; subscriber: string; temperature: number; txBytes: number; diff --git a/src/models/Device.ts b/src/models/Device.ts index a02508d..f33425b 100644 --- a/src/models/Device.ts +++ b/src/models/Device.ts @@ -39,6 +39,7 @@ export interface GatewayDevice { }; }; serialNumber: string; + simulated: boolean; subscriber: string; venue: string; } diff --git a/src/pages/Device/Wrapper.tsx b/src/pages/Device/Wrapper.tsx index b0ecdab..f78f2de 100644 --- a/src/pages/Device/Wrapper.tsx +++ b/src/pages/Device/Wrapper.tsx @@ -12,17 +12,13 @@ import { HStack, Portal, Spacer, - Tag, - TagLabel, - TagLeftIcon, - Tooltip, useBreakpoint, useColorModeValue, useDisclosure, useToast, } from '@chakra-ui/react'; +import { Circuitry, Heart, HeartBreak, LockSimple, LockSimpleOpen, WifiHigh, WifiSlash } from '@phosphor-icons/react'; import axios from 'axios'; -import { Heart, HeartBreak, LockSimple, LockSimpleOpen, WifiHigh, WifiSlash } from '@phosphor-icons/react'; import { useTranslation } from 'react-i18next'; import Masonry from 'react-masonry-css'; import { useNavigate } from 'react-router-dom'; @@ -39,8 +35,8 @@ import DeviceActionDropdown from 'components/Buttons/DeviceActionDropdown'; import { RefreshButton } from 'components/Buttons/RefreshButton'; import { Card } from 'components/Containers/Card'; import { CardHeader } from 'components/Containers/Card/CardHeader'; +import { ResponsiveTag } from 'components/Containers/ResponsiveTag'; import GlobalSearchBar from 'components/GlobalSearchBar'; -import FormattedDate from 'components/InformationDisplays/FormattedDate'; import { ConfigureModal } from 'components/Modals/ConfigureModal'; import { EventQueueModal } from 'components/Modals/EventQueueModal'; import FactoryResetModal from 'components/Modals/FactoryResetModal'; @@ -114,10 +110,11 @@ const DevicePageWrapper = ({ serialNumber }: Props) => { if (!getStatus.data) return null; return ( - - - {getStatus?.data?.connected ? t('common.connected') : t('common.disconnected')} - + ); }, [getStatus.data]); @@ -133,23 +130,12 @@ const DevicePageWrapper = ({ serialNumber }: Props) => { if (sanityValue === 100) color = 'green'; else if (sanityValue > 80) color = 'yellow'; } - return ( - - ) : ( - '' - ) - } - > - - - {sanity ? `${sanity}%` : t('common.unknown')} - - + ); }, [getStatus.data, getHealth.data]); @@ -158,21 +144,21 @@ const DevicePageWrapper = ({ serialNumber }: Props) => { if (getDevice.data.restrictionDetails?.developer) return ( - - - - - {t('devices.restricted')} {isCompact ? '' : '(Dev Mode)'} - - - + ); return ( - - - {t('devices.restricted')} - + ); }, [getDevice.data, isCompact]); @@ -189,6 +175,9 @@ const DevicePageWrapper = ({ serialNumber }: Props) => { {serialNumber} + {getDevice.data?.simulated ? ( + + ) : null} {connectedTag} {healthTag} {restrictedTag} @@ -239,6 +228,9 @@ const DevicePageWrapper = ({ serialNumber }: Props) => { {serialNumber} + {getDevice.data?.simulated ? ( + + ) : null} {connectedTag} {healthTag} {restrictedTag} diff --git a/src/pages/Devices/ListCard/index.tsx b/src/pages/Devices/ListCard/index.tsx index ef9d8e4..89f0f8f 100644 --- a/src/pages/Devices/ListCard/index.tsx +++ b/src/pages/Devices/ListCard/index.tsx @@ -81,7 +81,9 @@ const DeviceListCard = () => { const configureModalProps = useDisclosure(); const rebootModalProps = useDisclosure(); const scriptModal = useScriptModal(); - const tableController = useDataGrid({ tableSettingsId: 'gateway.devices.table' }); + const tableController = useDataGrid({ + tableSettingsId: 'gateway.devices.table', + }); const getCount = useGetDeviceCount({ enabled: true }); const getDevices = useGetDevices({ pageInfo: { @@ -133,14 +135,14 @@ const DeviceListCard = () => { h="35px" w="35px" borderRadius="50em" - bgColor={BADGE_COLORS[device.verifiedCertificate] ?? 'red'} + bgColor={BADGE_COLORS[device.simulated ? 'SIMULATED' : device.verifiedCertificate] ?? 'red'} alignItems="center" display="inline-flex" justifyContent="center" position="relative" > @@ -325,14 +327,15 @@ const DeviceListCard = () => { const temperatureCell = React.useCallback((device: DeviceWithStatus) => { if (!device.connected || device.temperature === 0) return
-
; + const temperature = device.temperature > 1000 ? device.temperature / 1000 : device.temperature; let colorScheme = 'red'; - if (device.temperature <= 85) colorScheme = 'yellow'; - if (device.temperature <= 75) colorScheme = 'green'; + if (temperature <= 85) colorScheme = 'yellow'; + if (temperature <= 75) colorScheme = 'green'; return (
- {fourDigitNumber(device.temperature)}°C + {fourDigitNumber(temperature)}°C
@@ -349,6 +352,10 @@ const DeviceListCard = () => { cell: (v) => badgeCell(v.cell.row.original), enableSorting: false, meta: { + columnSelectorOptions: { + label: 'Connection Badge', + }, + anchored: true, customWidth: '35px', alwaysShow: true, }, @@ -361,6 +368,7 @@ const DeviceListCard = () => { cell: (v) => serialCell(v.cell.row.original), enableSorting: false, meta: { + anchored: true, alwaysShow: true, customMaxWidth: '200px', customWidth: '130px', @@ -508,6 +516,24 @@ const DeviceListCard = () => { cell: (v) => uptimeCell(v.cell.row.original), enableSorting: false, }, + { + id: 'lastRecordedContact', + header: t('analytics.last_connected'), + footer: '', + accessorKey: 'lastRecordedContact', + cell: (v) => + dateCell( + v.cell.row.original.lastContact !== 0 + ? v.cell.row.original.lastContact + : v.cell.row.original.lastRecordedContact, + ), + enableSorting: false, + meta: { + headerOptions: { + tooltip: t('analytics.last_connected_tooltip'), + }, + }, + }, { id: 'lastContact', header: t('analytics.last_contact'),