diff --git a/src/common/SmartBreadcrumb.tsx b/src/common/SmartBreadcrumb.tsx
index 4d2c176..be1d0fe 100644
--- a/src/common/SmartBreadcrumb.tsx
+++ b/src/common/SmartBreadcrumb.tsx
@@ -7,11 +7,12 @@ import {
LinkProps,
Skeleton,
Theme,
+ Typography,
useTheme
} from "@mui/material";
import { Replay } from "@mui/icons-material";
import { useMobile } from "hooks";
-import { ReactNode, useEffect, useRef } from "react";
+import { ReactNode } from "react";
import { useLocation } from "react-router";
import { Link as RouterLink } from "react-router-dom";
import { or } from "utils/types";
@@ -57,11 +58,13 @@ interface Props {
paddingTop?: number;
paddingBottom?: number;
prependPaths?: string[];
+ state?: any;
}
interface LinkRouterProps extends LinkProps {
to: string;
replace?: boolean;
+ state?: any;
}
const LinkRouter = (props: LinkRouterProps) => ;
@@ -72,7 +75,7 @@ export default function SmartBreadcrumb(props: Props) {
const location = useLocation();
const isMobile = useMobile();
const classes = useStyles();
- const { loading, reload } = props;
+ const { loading, reload, state } = props;
const groupID = (): string => {
let index = prependPaths?.indexOf("groups")
@@ -289,7 +292,7 @@ export default function SmartBreadcrumb(props: Props) {
if (prependPaths) pathnames.splice(pathnames.length - 1, 0, ...prependPaths);
- pathnames.forEach((_value: any, index: any) => {
+ pathnames.forEach((value: any, index: any) => {
const lastPath = index === pathnames.length - 1;
const to = `/${pathnames.slice(0, index + 1).join("/")}`;
@@ -310,6 +313,21 @@ export default function SmartBreadcrumb(props: Props) {
links.push(linkComponent(to, label, lastPath));
}
+ if (value === "support") {
+ links.push(
+
+ Support
+
+ }
+ className={classes.chip}
+ />
+ )
+ }
+
});
return links;
};
@@ -324,6 +342,7 @@ export default function SmartBreadcrumb(props: Props) {
color={lastPath ? "textPrimary" : "textSecondary"}
variant="subtitle1"
to={to}
+ state={state}
sx={{ '&:hover': { color: getThemeType() === "dark" ? 'white' : "black" }}}
className={classes.textOverflow}>
{label}
diff --git a/src/common/StatusSen5x.tsx b/src/common/StatusSen5x.tsx
index 262f595..912f128 100644
--- a/src/common/StatusSen5x.tsx
+++ b/src/common/StatusSen5x.tsx
@@ -176,12 +176,12 @@ export default function StatusPlenum(props: Props) {
>
- {device.status.sen5x?.voc.toFixed(1)}%
+ V: {device.status.sen5x?.voc.toFixed(1)}%
- {device.status.sen5x?.nox.toFixed(1)}%
+ N: {device.status.sen5x?.nox.toFixed(1)}%
diff --git a/src/device/DeviceActions.tsx b/src/device/DeviceActions.tsx
index 152ba70..8356b6a 100644
--- a/src/device/DeviceActions.tsx
+++ b/src/device/DeviceActions.tsx
@@ -24,7 +24,8 @@ import {
AccountCircle as ObjectUsersIcon,
SupervisedUserCircle as ObjectTeamsIcon,
Sync as SyncDeviceIcon,
- Wifi as WifiIcon
+ Wifi as WifiIcon,
+ Visibility
} from "@mui/icons-material";
import { Skeleton } from "@mui/material";
// import Datadog from "assets/external/datadog.png";
@@ -47,7 +48,7 @@ import { Component, Device, deviceScope, Interaction } from "models";
// import { isShareableLink } from "pbHelpers/Device";
import { pond } from "protobuf-ts/pond";
import { useGlobalState } from "providers";
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
// import { useHistory, useRouteMatch } from "react-router";
import { isOffline } from "utils/environment";
import { or } from "utils/types";
@@ -71,6 +72,7 @@ import Connection from "./Connection";
import ComponentOrder from "component/ComponentOrder";
import ArcGISDeviceData from "./ArcGISDeviceData";
import UpgradeDevice from "./UpgradeDevice";
+import { DevicePageData } from "pages/Device";
const useStyles = makeStyles((_theme: Theme) => {
// const isMobile = useMobile()
@@ -169,6 +171,19 @@ export default function DeviceActions(props: Props) {
isJsonDataDialogOpen: false
});
+ const [devicePageData, setDevicePageData] = useState(undefined);
+
+ useEffect(() => {
+ let newPageData: DevicePageData = {
+ device: device,
+ components: components,
+ interactions: interactions,
+ preferences: preferences,
+ permissions: permissions,
+ }
+ setDevicePageData(newPageData)
+ }, [device, components, interactions, preferences, permissions])
+
const openDialog = (target: keyof DialogState) => {
let updatedDialogState = cloneDeep(dialogState);
updatedDialogState[target] = true;
@@ -495,9 +510,18 @@ export default function DeviceActions(props: Props) {
);
};
+ const showSupport = () => {
+ return user.allowedTo("provision") && !location.pathname.includes("support")
+ }
+
const buttons = () => {
return (
+ {showSupport() &&
+ navigate("support", { state: {device: device, devicePageData: devicePageData }})}>
+
+
+ }
{canWrite && user.allowedTo("provision") && }
{/* {!isShareableLink(match.params.deviceID) && ( */}
import("pages/DeviceHistory"));
const DevicePage = lazy(() => import("pages/Device"));
@@ -103,6 +104,10 @@ export default function Router() {
path="/:deviceID" // "/settings/basic"
element={}
/>
+ }
+ />
}
diff --git a/src/pages/Device.tsx b/src/pages/Device.tsx
index 702285e..911e9f0 100644
--- a/src/pages/Device.tsx
+++ b/src/pages/Device.tsx
@@ -22,6 +22,15 @@ import { or } from "utils";
import ComponentDiagnostics from "component/ComponentDiagnostics";
import DeviceWizard from "device/DeviceWizard";
+export interface DevicePageData {
+ device: Device;
+ components: Component[];
+ interactions: Interaction[];
+ permissions: pond.Permission[];
+ preferences: pond.DevicePreferences;
+ // componentPreferences: Map
+}
+
export default function DevicePage() {
const deviceAPI = useDeviceAPI()
const snackbar = useSnackbar()
@@ -30,6 +39,7 @@ export default function DevicePage() {
const groupID = useParams<{ groupID: string }>()?.groupID ?? "";
const { state } = useLocation();
const [{ as, team, user }] = useGlobalState()
+ // console.log(state)
const [device, setDevice] = useState(state?.device ? Device.create(state.device) : Device.create())
const [loading, setLoading] = useState(false)
const [permissions, setPermissions] = useState([])
@@ -51,11 +61,21 @@ export default function DevicePage() {
const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false)
+ // const [devicePageData, setDevicePageData] = useState(pond.GetDevicePageDataResponse.create())
+
const loadDevice = () => {
- // console.log("load device page data")
if (loading) return
+ if (state?.devicePageData) {
+ setDevice(Device.create(state.devicePageData.device))
+ setComponents(state.devicePageData.components.map((comp: pond.Component) => Component.create(comp)))
+ setInteractions(state.devicePageData.interactions.map((inter: pond.Interaction) => Interaction.create(inter)))
+ setPreferences(state.devicePageData.preferences)
+ setPermissions(state.devicePageData.permissions)
+ return
+ }
setLoading(true)
deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes(), as).then(resp => {
+ // setDevicePageData(resp.data)
let device = Device.any(resp.data.device)
// console.log(resp.data.device)
setDevice(device)
@@ -418,19 +438,9 @@ export default function DevicePage() {
-
+
- setAddComponentManualDialogOpen(false)}
- />
- {user.hasFeature("developer") === true &&
-
- }
+ {value === index && <>{children}>}
+
+ );
+}
+
+export default function DeviceSupport() {
+ const deviceID = +(useParams<{ deviceID: string }>()?.deviceID ?? "0");
+
+ const deviceAPI = useDeviceAPI()
+ const snackbar = useSnackbar()
+ const theme = useTheme()
+
+ const { state } = useLocation();
+ const [{ user, as }] = useGlobalState()
+ const [device, setDevice] = useState(Device.create())
+ const [devicePageData, setDevicePageData] = useState(state?.devicePageData ? state.devicePageData : undefined)
+ const [tabNumber, setTabNumber] = useState(0)
+ const [newCap, setNewCap] = useState(0);
+ const [datacap, setDatacap] = useState();
+ const [isOver, setIsOver] = useState(undefined);
+ const [pauseCheck, setPauseCheck] = useState(undefined);
+ const [inputFirmware, setInputFirmware] = useState("");
+ const [platform, setPlatform] = useState(
+ pond.DevicePlatform.DEVICE_PLATFORM_INVALID
+ );
+ const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false)
+ const [loading, setLoading] = useState(false)
+
+ // useEffect(() => {
+ // if (state?.device) return
+ // deviceAPI.get(deviceID, undefined, getContextKeys(), getContextTypes()).then(resp => {
+ // setDevice(Device.create(resp.data))
+ // })
+ // }, [deviceID, state?.device])
+
+ useEffect(() => {
+ if (state?.devicePageData) {
+ setDevice(Device.create(state?.devicePageData.device))
+ return
+ }
+ setLoading(true)
+ deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes()).then(resp => {
+ setDevice(resp.data?.device ? Device.create(resp.data?.device) : Device.create())
+ setDevicePageData({
+ device: resp.data.device ? Device.create(resp.data.device) : Device.create(),
+ components: resp.data.components.map(comp => Component.create(comp)),
+ interactions: resp.data.interactions.map(interaction => Interaction.create(interaction)),
+ permissions: resp.data.permissions,
+ preferences: resp.data.user?.preferences as pond.DevicePreferences
+ })
+ }).finally(() => {
+ setLoading(false)
+ })
+ }, [deviceID, state?.devicePageData])
+
+ const handleChange = (_event: React.ChangeEvent<{}>, newValue: number) => {
+ setTabNumber(newValue);
+ };
+
+ const resetQuackCount = () => {
+ deviceAPI.resetQuackCount(device.id()).then(resp => console.log(resp));
+ };
+
+ const resetQuackCountTx = () => {
+ deviceAPI.resetQuackCountTx(device.id()).then(resp => console.log(resp));
+ };
+
+ const resetQuackCountTx1000 = () => {
+ deviceAPI.resetQuackCountTx1000(device.id()).then(resp => console.log(resp));
+ };
+
+ const clearPending = () => {
+ deviceAPI.clearPending(device.id()).then(resp => {
+ console.log(resp);
+ });
+ };
+
+ const setFirmwareVersion = (firmwareVersion: string) => {
+ let status = device.status;
+ status.firmwareVersion = firmwareVersion;
+ deviceAPI.updateStatus(device.id(), status).then(() => {
+ snackbar.info("Firmware version set to " + firmwareVersion);
+ });
+ };
+
+ const updateDevicePlatform = (firmwareVersion: pond.DevicePlatform) => {
+ let settings = device.settings;
+ settings.platform = firmwareVersion;
+ deviceAPI.update(device.id(), settings).then(() => {
+ snackbar.info("Platform set to " + firmwareVersion.toString());
+ });
+ };
+
+ const getDatacap = () => {
+ deviceAPI.getDatacap(device.id()).then(resp => {
+ //console.log(resp);
+ setDatacap(resp.data.overlimit);
+ });
+ };
+
+ const updateDatacap = () => {
+ deviceAPI.setDatacap(device.id(), newCap).then(resp => {
+ console.log(resp);
+ });
+ };
+
+ const checkLimit = () => {
+ deviceAPI.isOverLimit(device.id()).then(resp => {
+ //console.log(resp);
+ setIsOver(resp.data);
+ });
+ };
+
+ const checkIfPaused = () => {
+ deviceAPI.isPaused(device.id()).then(resp => {
+ //console.log(resp);
+ setPauseCheck(resp.data);
+ });
+ };
+
+ const pauseDevice = () => {
+ deviceAPI.pause(device.id()).then(resp => {
+ console.log(resp);
+ });
+ };
+
+ const resumeDevice = () => {
+ deviceAPI.resume(device.id()).then(resp => {
+ console.log(resp);
+ });
+ };
+
+ const toggleNotificationPreference = () => {
+ let updatedPreferences = cloneDeep(devicePageData.preferences);
+ updatedPreferences.notify = !devicePageData.preferences.notify;
+ let updatedDevicePageData = cloneDeep(devicePageData)
+ updatedDevicePageData.preferences = updatedPreferences
+ deviceAPI
+ .updatePreferences(deviceID, updatedPreferences, getContextKeys(), getContextTypes(), as)
+ .then(() => setDevicePageData(updatedDevicePageData))
+ .catch(() => {
+ snackbar.error(
+ "Error occured while " +
+ (devicePageData.preferences.notify ? "enabling" : "disabling") +
+ " notifications"
+ );
+ });
+ };
+
+ if (!devicePageData) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+
+ {}}
+ availablePositions={FindAvailablePositions(devicePageData.components, device.settings.product).availability}
+ availableOffsets={new Map()}
+ components={[...devicePageData.components.map(comp => Component.any(comp))]}
+ interactions={devicePageData.interactions.map(inter => Interaction.any(inter))}
+ />
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+ General Options:
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setAddComponentManualDialogOpen(false)}
+ />
+ {user.hasFeature("developer") === true &&
+
+ }
+
+
+
+ setInputFirmware(e.currentTarget.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OPI Cable Functions:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setNewCap(+e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+ {datacap && datacap / 1000 / 1000}Mb
+
+
+
+
+
+
+
+
+ {isOver === undefined ? "Click to Check" : isOver ? "Over Limit" : "Under Limit"}
+
+
+
+
+
+
+
+
+
+ {pauseCheck === undefined ? "Click to Check" : pauseCheck ? "Paused" : "Running"}
+
+
+
+
+
+
+
+
+
+
+
+ History Not imported yet
+ {/* */}
+
+
+ )
+}
\ No newline at end of file