diff --git a/index.html b/index.html
index da05f1e..31b4ca1 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
Adaptive Dashboard
+ Adaptive Dashboard
diff --git a/package.json b/package.json
index efb9aa8..8e3ebd3 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"start": "VITE_AUTH0_CLIENT_ID=5pUCkl2SfogkWmM244UDcLEUOp8EFdHd VITE_AUTH0_AUDIENCE=api.brandxtech.ca VITE_APP_API_URL=https://api.brandxtech.ca/v1 VITE_APP_WS_URL=ws://api.brandxtech.ca/v1/live vite",
"start-local": "VITE_AUTH0_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 VITE_AUTH0_AUDIENCE=bxt-dev.api.adaptiveagriculture.ca VITE_APP_API_URL=http://localhost:50052/v1 VITE_APP_WS_URL=ws://localhost:50052/v1/live VITE_APP_BILLING_URL=http://localhost:50053/v1 VITE_APP_RECLUSE_URL=http://localhost:50054/v1 VITE_APP_GITLAB_URL=http://localhost:50055/v1 vite",
"start-dev": "VITE_AUTH0_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 VITE_APP_API_URL=https://bxt-dev.api.adaptiveagriculture.ca/v1 VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=bxt-dev.api.adaptiveagriculture.ca VITE_AUTH0_DEV_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 vite",
- "start-staging": "VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1 VITE_APP_WS_URL=ws://stagingapi.brandxtech.ca/v1/live VITE_APP_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_APP_AUTH0_AUDIENCE=stagingapi.brandxtech.ca vite",
+ "start-staging": "VITE_LOCAL_STAGING=true VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1 VITE_APP_WS_URL=ws://stagingapi.brandxtech.ca/v1/live VITE_APP_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_APP_AUTH0_AUDIENCE=stagingapi.brandxtech.ca vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
diff --git a/public/Staging/android-chrome-192x192.png b/public/Staging/android-chrome-192x192.png
new file mode 100644
index 0000000..cbd70d4
Binary files /dev/null and b/public/Staging/android-chrome-192x192.png differ
diff --git a/public/Staging/android-chrome-512x512.png b/public/Staging/android-chrome-512x512.png
new file mode 100644
index 0000000..5d93e84
Binary files /dev/null and b/public/Staging/android-chrome-512x512.png differ
diff --git a/public/Staging/apple-touch-icon.png b/public/Staging/apple-touch-icon.png
new file mode 100644
index 0000000..0914e18
Binary files /dev/null and b/public/Staging/apple-touch-icon.png differ
diff --git a/public/Staging/browserconfig.xml b/public/Staging/browserconfig.xml
new file mode 100644
index 0000000..d416bc5
--- /dev/null
+++ b/public/Staging/browserconfig.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ #ffffff
+
+
+
diff --git a/public/Staging/favicon-16x16.png b/public/Staging/favicon-16x16.png
new file mode 100644
index 0000000..aa87654
Binary files /dev/null and b/public/Staging/favicon-16x16.png differ
diff --git a/public/Staging/favicon-32x32.png b/public/Staging/favicon-32x32.png
new file mode 100644
index 0000000..a606ca0
Binary files /dev/null and b/public/Staging/favicon-32x32.png differ
diff --git a/public/Staging/favicon.ico b/public/Staging/favicon.ico
new file mode 100644
index 0000000..54725be
Binary files /dev/null and b/public/Staging/favicon.ico differ
diff --git a/public/Staging/manifest.json b/public/Staging/manifest.json
new file mode 100644
index 0000000..aad40f6
--- /dev/null
+++ b/public/Staging/manifest.json
@@ -0,0 +1,26 @@
+{
+ "name": "Brand X Technologies",
+ "short_name": "BXT",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "48x48",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "start_url": "/",
+ "theme_color": "#272727",
+ "background_color": "#424242",
+ "display": "standalone",
+ "orientation": "any"
+}
diff --git a/public/Staging/mstile-150x150.png b/public/Staging/mstile-150x150.png
new file mode 100644
index 0000000..e91210a
Binary files /dev/null and b/public/Staging/mstile-150x150.png differ
diff --git a/public/Staging/safari-pinned-tab.svg b/public/Staging/safari-pinned-tab.svg
new file mode 100644
index 0000000..5f44534
--- /dev/null
+++ b/public/Staging/safari-pinned-tab.svg
@@ -0,0 +1,2198 @@
+
+
+
diff --git a/src/app/App.tsx b/src/app/App.tsx
index 46e7121..16bb781 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -37,6 +37,11 @@ function App() {
faviconDocument.href = "BrandXTechnologies/favicon.ico"
})
+ // Set title
+ const titleElement = document.getElementById("title-id") as HTMLElement;
+ if (titleElement) titleElement.textContent = whiteLabel.name;
+
+
let domain: string | undefined = import.meta.env.VITE_AUTH0_CLIENT_DOMAIN;
let audience: string | undefined = import.meta.env.VITE_AUTH0_AUDIENCE;
diff --git a/src/common/ButtonGroup.tsx b/src/common/ButtonGroup.tsx
index f23bc0a..f1d80f4 100644
--- a/src/common/ButtonGroup.tsx
+++ b/src/common/ButtonGroup.tsx
@@ -72,14 +72,14 @@ const useStyles = makeStyles((theme: Theme) => {
marginLeft: theme.spacing(0.05),
marginRight: theme.spacing(0.05),
"&:hover": {
- backgroundColor: getSecondaryColour(), //use the colour of the whitelabel
- //backgroundColor: "gold",
+ //backgroundColor: getSecondaryColour(), //use the colour of the whitelabel
+ backgroundColor: "gold", //for now Dustin just wants it to be gold
color: "black",
}
},
buttonToggled: {
- backgroundColor: getSecondaryColour(), //use the colour of the whitelabel
- //backgroundColor: "gold",
+ //backgroundColor: getSecondaryColour(), //use the colour of the whitelabel
+ backgroundColor: "gold", //for now Dustin just wants it to be gold
color: "black",
borderRadius: 24,
fontWeight: "bold"
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
diff --git a/src/services/whiteLabel.ts b/src/services/whiteLabel.ts
index 22de3bf..d14ab51 100644
--- a/src/services/whiteLabel.ts
+++ b/src/services/whiteLabel.ts
@@ -70,7 +70,7 @@ interface WhiteLabel {
// };
const STAGING_WHITELABEL: WhiteLabel = {
- name: import.meta.env.VITE_APP_WEBSITE_TITLE,
+ name: "Staging",
primaryColour: import.meta.env.VITE_APP_PRIMARY_COLOUR,
secondaryColour: import.meta.env.VITE_APP_SECONDARY_COLOUR,
signatureColour: import.meta.env.VITE_APP_SIGNATURE_COLOUR,
@@ -276,7 +276,7 @@ export function getWhitelabel(): WhiteLabel {
if (window.location.origin.includes("bxt-dev")) {
return BXT_WHITE_LABEL;
}
- if (window.location.origin.includes("staging")) {
+ if (window.location.origin.includes("staging") || import.meta.env.VITE_LOCAL_STAGING=='true') {
return STAGING_WHITELABEL;
}
const whiteLabelKeys = Array.from(whitelabels.keys());