Merge branch 'staging_environment' into hybrid_inventory_control
15
.env
|
|
@ -8,16 +8,15 @@ VITE_AUTH0_AUDIENCE=api.brandxtech.ca
|
||||||
VITE_AUTH0_DEV_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1
|
VITE_AUTH0_DEV_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1
|
||||||
VITE_AUTH0_STAGING_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP
|
VITE_AUTH0_STAGING_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP
|
||||||
|
|
||||||
# VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP
|
VITE_AUTH0_BXT_CLIENT_ID=sLnqOu40uWfQT1lYSDYj2wYmlLEHRB74
|
||||||
# VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca
|
VITE_AUTH0_ADAPTIVE_AGRICULTURE_CLIENT_ID=5pUCkl2SfogkWmM244UDcLEUOp8EFdHd
|
||||||
# VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com
|
VITE_AUTH0_ADAPTIVE_CONSTRUCTION_CLIENT_ID=32rABabJzXRvJiWivTmeKFgwFiqh4ok7
|
||||||
# VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1
|
VITE_AUTH0_AEROGROW_CLIENT_ID=KHl9ooUt1nia1RYw5n224dyggCXdbsSd
|
||||||
# VITE_APP_WS_URL=ws://stagingapi.brandxtech.ca/v1/live
|
VITE_AUTH0_MIVENT_CLIENT_ID=VNALE7RW6l3dY5uYcxgwElZV0lcT25Fg
|
||||||
# VITE_APP_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com
|
VITE_AUTH0_OMNIAIR_CLIENT_ID=IblmarD8wFafiD6doxTmOHQ6Bx3L9wWl
|
||||||
# VITE_APP_AUTH0_AUDIENCE=stagingapi.brandxtech.ca
|
|
||||||
|
|
||||||
#Branding (Default theme)
|
#Branding (Default theme)
|
||||||
VITE_APP_WEBSITE_TITLE="Adaptive Dashboard"
|
VITE_APP_WEBSITE_TITLE="Adaptive Dashboard"
|
||||||
VITE_APP_PRIMARY_COLOUR=blue
|
VITE_APP_PRIMARY_COLOUR=blue
|
||||||
VITE_APP_SECONDARY_COLOUR=blueGrey
|
VITE_APP_SECONDARY_COLOUR=blueGrey
|
||||||
VITE_APP_SIGNATURE_COLOUR="#323232"
|
VITE_APP_SIGNATURE_COLOUR="#323232"
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ dependencies:production:
|
||||||
only:
|
only:
|
||||||
refs:
|
refs:
|
||||||
- master
|
- master
|
||||||
before_script:
|
# before_script:
|
||||||
- npm -g config set user root
|
# - npm -g config set user root
|
||||||
script:
|
script:
|
||||||
- npm ci
|
- npm ci
|
||||||
cache:
|
cache:
|
||||||
|
|
@ -28,17 +28,17 @@ dependencies:production:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules/
|
||||||
|
|
||||||
test:
|
# test:
|
||||||
image: node:$NODE_VERSION
|
# image: node:$NODE_VERSION
|
||||||
stage: setup
|
# stage: setup
|
||||||
dependencies:
|
# dependencies:
|
||||||
- dependencies:production
|
# - dependencies:production
|
||||||
only:
|
# only:
|
||||||
- master
|
# - master
|
||||||
before_script:
|
# before_script:
|
||||||
- npm run lint
|
# - npm run lint
|
||||||
script:
|
# script:
|
||||||
- CI=true npm run test
|
# - CI=true npm run test
|
||||||
|
|
||||||
setup:production:
|
setup:production:
|
||||||
image: node:$NODE_VERSION
|
image: node:$NODE_VERSION
|
||||||
|
|
@ -197,6 +197,7 @@ staging:deploy:
|
||||||
DEPLOY_IMAGE: ${CI_REGISTRY}/brandx/bxt-app/production:staging
|
DEPLOY_IMAGE: ${CI_REGISTRY}/brandx/bxt-app/production:staging
|
||||||
DOCKER_HOST: ssh://deploy@staging.brandxtech.ca
|
DOCKER_HOST: ssh://deploy@staging.brandxtech.ca
|
||||||
DEPLOY_TARGET: deploy@staging.brandxtech.ca
|
DEPLOY_TARGET: deploy@staging.brandxtech.ca
|
||||||
|
VITE_MAPBOX_ACCESS_TOKEN: $MAPBOX_ACCESS_TOKEN
|
||||||
only:
|
only:
|
||||||
- staging_environment
|
- staging_environment
|
||||||
environment:
|
environment:
|
||||||
|
|
@ -206,6 +207,7 @@ staging:deploy:
|
||||||
- "apk --update add openssh-client"
|
- "apk --update add openssh-client"
|
||||||
- "eval $(ssh-agent -s)"
|
- "eval $(ssh-agent -s)"
|
||||||
- "echo \"$STAGING_SSH_PRIV_KEY\" | tr -d '\r' | ssh-add - "
|
- "echo \"$STAGING_SSH_PRIV_KEY\" | tr -d '\r' | ssh-add - "
|
||||||
|
- "echo \"$MAPBOX_ACCESS_TOKEN\""
|
||||||
# - "ssh -Mo StrictHostKeyChecking=no -S deploy-ctrl-socket -fnNTL /var/run/docker.sock:/var/run/docker.sock ${DEPLOY_TARGET}"
|
# - "ssh -Mo StrictHostKeyChecking=no -S deploy-ctrl-socket -fnNTL /var/run/docker.sock:/var/run/docker.sock ${DEPLOY_TARGET}"
|
||||||
# - "ssh -S deploy-ctrl-socket -O check ${DEPLOY_TARGET}"
|
# - "ssh -S deploy-ctrl-socket -O check ${DEPLOY_TARGET}"
|
||||||
- mkdir -p ~/.ssh
|
- mkdir -p ~/.ssh
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/x-icon" id="favicon-link" />
|
<link rel="icon" type="image/x-icon" id="favicon-link" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Adaptive Dashboard</title>
|
<title id="title-id">Adaptive Dashboard</title>
|
||||||
<link rel="manifest" id="manifest-link" />
|
<link rel="manifest" id="manifest-link" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
765
package-lock.json
generated
12
package.json
|
|
@ -4,16 +4,16 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"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-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-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",
|
"build": "tsc -b && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"build:staging": "VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1 VITE_APP_WS_URL=wss://stagingapi.brandxtech.ca/v1/live NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_STAGING} VITE_APP_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_APP_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_APP_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_APP_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_APP_CNHI_SCOPES=${CNHI_SCOPES} VITE_APP_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_APP_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_APP_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_APP_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_APP_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_APP_JD_SCOPES=${JD_SCOPES} VITE_APP_JD_STATE=${JD_STATE} vite build",
|
"build:staging": "VITE_AUTH0_CLIENT_DOMAIN=adaptivestaging.us.auth0.com VITE_AUTH0_AUDIENCE=stagingapi.brandxtech.ca VITE_AUTH0_CLIENT_ID=3ib460VvLwdeyse5iUSQfxkVdQaUmphP VITE_APP_API_URL=https://stagingapi.brandxtech.ca/v1 VITE_APP_WS_URL=wss://stagingapi.brandxtech.ca/v1/live NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_STAGING} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build",
|
||||||
"build:development": "VITE_AUTH0_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 VITE_AUTH0_AUDIENCE=bxt-dev.api.adaptiveagriculture.ca VITE_APP_API_URL=https://bxt-dev.api.adaptiveagriculture.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_DEVELOPMENT} VITE_APP_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_APP_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_APP_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_APP_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_APP_CNHI_SCOPES=${CNHI_SCOPES} VITE_APP_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_APP_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_APP_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_APP_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_APP_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_APP_JD_SCOPES=${JD_SCOPES} VITE_APP_JD_STATE=${JD_STATE} vite build",
|
"build:development": "VITE_AUTH0_CLIENT_ID=dzJTpqIeMA4Rwk4xujtwPbAO3TY32bM1 VITE_AUTH0_AUDIENCE=bxt-dev.api.adaptiveagriculture.ca VITE_APP_API_URL=https://bxt-dev.api.adaptiveagriculture.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_DEVELOPMENT} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build",
|
||||||
"build:production": "NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_PRODUCTION} VITE_APP_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_APP_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_APP_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_APP_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_APP_CNHI_SCOPES=${CNHI_SCOPES} VITE_APP_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_APP_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_APP_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_APP_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_APP_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_APP_JD_SCOPES=${JD_SCOPES} VITE_APP_JD_STATE=${JD_STATE} vite build",
|
"build:production": "VITE_AUTH0_CLIENT_ID=5pUCkl2SfogkWmM244UDcLEUOp8EFdHd VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=api.brandxtech.ca VITE_APP_API_URL=https://api.brandxtech.ca/v1 NODE_OPTIONS=--max_old_space_size=4096 VITE_APP_GOOGLE_API_KEY=${GOOGLE_API_KEY} VITE_APP_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY_PRODUCTION} VITE_MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN} VITE_CNHI_CLIENT_ID=${CNHI_CLIENT_ID} VITE_CNHI_AUTHORIZE_URL=${CNHI_AUTHORIZE_URL} VITE_CNHI_REDIRECT_URI=${CNHI_REDIRECT_URI} VITE_CNHI_SCOPES=${CNHI_SCOPES} VITE_CNHI_CONNECTION=${CNHI_CONNECTION} VITE_CNHI_AUDIENCE=${CNHI_AUDIENCE} VITE_APP_IMAGE4IO_USERNAME=${IMAGE4IO_USERNAME} VITE_APP_IMAGE4IO_PASSWORD=${IMAGE4IO_PASSWORD} VITE_JD_CLIENT_ID=${JD_CLIENT_ID} VITE_JD_AUTHORIZE_URL=${JD_AUTHORIZE_URL} VITE_JD_REDIRECT_URI=${JD_REDIRECT_URI} VITE_JD_SCOPES=${JD_SCOPES} VITE_JD_STATE=${JD_STATE} VITE_OPEN_WEATHERMAP={OPEN_WEATHERMAP} vite build",
|
||||||
"build:offline": "npx env-cmd offline,whitelabel npm run build",
|
"build:offline": "npx env-cmd offline,whitelabel npm run build",
|
||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
|
|
@ -39,7 +39,6 @@
|
||||||
"@turf/area": "^7.2.0",
|
"@turf/area": "^7.2.0",
|
||||||
"@turf/turf": "^7.2.0",
|
"@turf/turf": "^7.2.0",
|
||||||
"@types/classnames": "^2.3.0",
|
"@types/classnames": "^2.3.0",
|
||||||
"@types/mapbox-gl": "^3.4.1",
|
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"deep-object-diff": "^1.1.9",
|
"deep-object-diff": "^1.1.9",
|
||||||
|
|
@ -80,6 +79,7 @@
|
||||||
"@types/lodash": "^4.17.13",
|
"@types/lodash": "^4.17.13",
|
||||||
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
||||||
"@types/mapbox__mapbox-gl-geocoder": "^5.0.0",
|
"@types/mapbox__mapbox-gl-geocoder": "^5.0.0",
|
||||||
|
"@types/mapbox-gl": "^3.4.1",
|
||||||
"@types/react": "^18.3.10",
|
"@types/react": "^18.3.10",
|
||||||
"@types/react-beautiful-dnd": "^13.1.8",
|
"@types/react-beautiful-dnd": "^13.1.8",
|
||||||
"@types/react-color": "^3.0.13",
|
"@types/react-color": "^3.0.13",
|
||||||
|
|
|
||||||
BIN
public/Staging/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
public/Staging/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/Staging/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
9
public/Staging/browserconfig.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square150x150logo src="/mstile-150x150.png"/>
|
||||||
|
<TileColor>#ffffff</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
||||||
BIN
public/Staging/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/Staging/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/Staging/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
26
public/Staging/manifest.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
BIN
public/Staging/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
2198
public/Staging/safari-pinned-tab.svg
Normal file
|
After Width: | Height: | Size: 144 KiB |
|
|
@ -37,19 +37,16 @@ function App() {
|
||||||
faviconDocument.href = "BrandXTechnologies/favicon.ico"
|
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 domain: string | undefined = import.meta.env.VITE_AUTH0_CLIENT_DOMAIN;
|
||||||
let audience: string | undefined = import.meta.env.VITE_AUTH0_AUDIENCE;
|
let audience: string | undefined = import.meta.env.VITE_AUTH0_AUDIENCE;
|
||||||
|
|
||||||
// let client_id = whiteLabel.auth0ClientId
|
let client_id = whiteLabel.auth0ClientId
|
||||||
let client_id = import.meta.env.VITE_AUTH0_CLIENT_ID;
|
if (!client_id) client_id = import.meta.env.VITE_AUTH0_CLIENT_ID;
|
||||||
// if (!client_id) client_id = import.meta.env.VITE_AUTH0_CLIENT_ID;
|
|
||||||
|
|
||||||
//check the url for a code before auth0 causes a login re-direct
|
|
||||||
// if (window.location.pathname !== "/callback") {
|
|
||||||
// //set the code into local storage
|
|
||||||
// let code = new URLSearchParams(window.location.search).get("code");
|
|
||||||
// sessionStorage.setItem("code", code || "")
|
|
||||||
// }
|
|
||||||
|
|
||||||
const skipCallbacks = [
|
const skipCallbacks = [
|
||||||
"/johndeere",
|
"/johndeere",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Box, Theme, ToggleButton, ToggleButtonGroup, darken } from "@mui/material";
|
import { Box, Theme, ToggleButton, ToggleButtonGroup, darken } from "@mui/material";
|
||||||
import { makeStyles, withStyles } from "@mui/styles";
|
import { makeStyles, withStyles } from "@mui/styles";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
import { ExtractMoisture } from "grain";
|
import { ExtractMoisture } from "grain";
|
||||||
import { GrainCable } from "models/GrainCable";
|
import { GrainCable } from "models/GrainCable";
|
||||||
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
||||||
|
|
@ -212,54 +213,54 @@ export default function BinSVGV2(props: Props) {
|
||||||
const [extraDetails, setExtraDetails] = useState<"temps" | "hums">("temps");
|
const [extraDetails, setExtraDetails] = useState<"temps" | "hums">("temps");
|
||||||
const [nodeWarning, setNodeWarning] = useState(false);
|
const [nodeWarning, setNodeWarning] = useState(false);
|
||||||
|
|
||||||
const StyledToggleButtonGroup = withStyles(theme => ({
|
// const StyledToggleButtonGroup = withStyles(theme => ({
|
||||||
grouped: {
|
// grouped: {
|
||||||
margin: theme.spacing(-0.5),
|
// margin: theme.spacing(-0.5),
|
||||||
border: "none",
|
// border: "none",
|
||||||
padding: theme.spacing(1),
|
// padding: theme.spacing(1),
|
||||||
"&:not(:first-child):not(:last-child)": {
|
// "&:not(:first-child):not(:last-child)": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.5),
|
// marginRight: theme.spacing(0.5),
|
||||||
marginLeft: theme.spacing(0.5)
|
// marginLeft: theme.spacing(0.5)
|
||||||
},
|
// },
|
||||||
"&:first-child": {
|
// "&:first-child": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginLeft: theme.spacing(0.25)
|
// marginLeft: theme.spacing(0.25)
|
||||||
},
|
// },
|
||||||
"&:last-child": {
|
// "&:last-child": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.25)
|
// marginRight: theme.spacing(0.25)
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: darken(
|
// backgroundColor: darken(
|
||||||
theme.palette.background.paper,
|
// theme.palette.background.paper,
|
||||||
getThemeType() === "light" ? 0.05 : 0.25
|
// getThemeType() === "light" ? 0.05 : 0.25
|
||||||
),
|
// ),
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
content: "border-box"
|
// content: "border-box"
|
||||||
}
|
// }
|
||||||
}))(ToggleButtonGroup);
|
// }))(ToggleButtonGroup);
|
||||||
|
|
||||||
const StyledToggle = withStyles({
|
// const StyledToggle = withStyles({
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: "transparent",
|
// backgroundColor: "transparent",
|
||||||
overflow: "visible",
|
// overflow: "visible",
|
||||||
content: "content-box",
|
// content: "content-box",
|
||||||
"&$selected": {
|
// "&$selected": {
|
||||||
backgroundColor: "gold",
|
// backgroundColor: "gold",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
fontWeight: "bold"
|
// fontWeight: "bold"
|
||||||
},
|
// },
|
||||||
"&$selected:hover": {
|
// "&$selected:hover": {
|
||||||
backgroundColor: "rgb(255, 255, 0)",
|
// backgroundColor: "rgb(255, 255, 0)",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24
|
// borderRadius: 24
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
selected: {}
|
// selected: {}
|
||||||
})(ToggleButton);
|
// })(ToggleButton);
|
||||||
|
|
||||||
const gradients = () => {
|
const gradients = () => {
|
||||||
const burning = "#8a1f0d";
|
const burning = "#8a1f0d";
|
||||||
|
|
@ -832,7 +833,6 @@ export default function BinSVGV2(props: Props) {
|
||||||
cableGrainEndY = cableGrainStartY;
|
cableGrainEndY = cableGrainStartY;
|
||||||
}
|
}
|
||||||
if (cableEstimate && cables.length > 0 && cableGrainPathPoints.length === cables.length) {
|
if (cableEstimate && cables.length > 0 && cableGrainPathPoints.length === cables.length) {
|
||||||
console.log("calc cable path");
|
|
||||||
let cablePath = cableGrainEstimate(
|
let cablePath = cableGrainEstimate(
|
||||||
cableGrainPathPoints,
|
cableGrainPathPoints,
|
||||||
spacingX,
|
spacingX,
|
||||||
|
|
@ -1048,10 +1048,26 @@ export default function BinSVGV2(props: Props) {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{showTempHum && (
|
{showTempHum && (
|
||||||
<Box
|
<Box
|
||||||
|
display="flex"
|
||||||
|
alignContent="center"
|
||||||
|
justifyContent="center"
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
//visibility={showTempHum ? "visible" : "hidden"}
|
//visibility={showTempHum ? "visible" : "hidden"}
|
||||||
className={!isFullScreen ? classes.smallToggle : classes.fullToggle}>
|
className={!isFullScreen ? classes.smallToggle : classes.fullToggle}>
|
||||||
<StyledToggleButtonGroup
|
<ButtonGroup
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: isFullScreen ? "Temperature" : "Temp",
|
||||||
|
function: () => setExtraDetails("temps")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: grainType ? (isFullScreen ? "Moisture" : "EMC") : isFullScreen ? "Humidity" : "Hum",
|
||||||
|
function: () => setExtraDetails("hums")
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
toggle
|
||||||
|
/>
|
||||||
|
{/* <StyledToggleButtonGroup
|
||||||
value={extraDetails}
|
value={extraDetails}
|
||||||
exclusive
|
exclusive
|
||||||
size="small"
|
size="small"
|
||||||
|
|
@ -1068,7 +1084,7 @@ export default function BinSVGV2(props: Props) {
|
||||||
aria-label="Node Humidity">
|
aria-label="Node Humidity">
|
||||||
{grainType ? (isFullScreen ? "Moisture" : "EMC") : isFullScreen ? "Humidity" : "Hum"}
|
{grainType ? (isFullScreen ? "Moisture" : "EMC") : isFullScreen ? "Humidity" : "Hum"}
|
||||||
</StyledToggle>
|
</StyledToggle>
|
||||||
</StyledToggleButtonGroup>
|
</StyledToggleButtonGroup> */}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box
|
<Box
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,7 @@ export default function BinStorageConditions(props: Props) {
|
||||||
alignContent="center"
|
alignContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="space-between">
|
justifyContent="space-between">
|
||||||
<Grid size={{ xs: 6 }}>
|
<Grid size={{ xs: 8 }}>
|
||||||
<Box display="flex" alignContent="center" alignItems="center">
|
<Box display="flex" alignContent="center" alignItems="center">
|
||||||
<TemperatureIcon />
|
<TemperatureIcon />
|
||||||
<Typography noWrap style={{ fontWeight: 650 }}>
|
<Typography noWrap style={{ fontWeight: 650 }}>
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ import { ExtractMoisture } from "grain";
|
||||||
import SearchSelect, { Option } from "common/SearchSelect";
|
import SearchSelect, { Option } from "common/SearchSelect";
|
||||||
import Edit from "@mui/icons-material/Edit";
|
import Edit from "@mui/icons-material/Edit";
|
||||||
import { makeStyles, styled } from "@mui/styles";
|
import { makeStyles, styled } from "@mui/styles";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -280,54 +281,54 @@ export default function BinVisualizer(props: Props) {
|
||||||
const [storageDate, setStorageDate] = useState(moment().format("YYYY-MM-DD"));
|
const [storageDate, setStorageDate] = useState(moment().format("YYYY-MM-DD"));
|
||||||
const [storageTime, setStorageTime] = useState(moment().format("HH:mm"));
|
const [storageTime, setStorageTime] = useState(moment().format("HH:mm"));
|
||||||
|
|
||||||
const StyledToggle = styled(ToggleButton)(({ theme }: { theme: Theme }) => ({
|
// const StyledToggle = styled(ToggleButton)(({ theme }: { theme: Theme }) => ({
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: "transparent",
|
// backgroundColor: "transparent",
|
||||||
overflow: "visible",
|
// overflow: "visible",
|
||||||
content: "content-box",
|
// content: "content-box",
|
||||||
"&$selected": {
|
// "&$selected": {
|
||||||
backgroundColor: "gold",
|
// backgroundColor: "gold",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
fontWeight: "bold"
|
// fontWeight: "bold"
|
||||||
},
|
// },
|
||||||
"&$selected:hover": {
|
// "&$selected:hover": {
|
||||||
backgroundColor: "rgb(255, 255, 0)",
|
// backgroundColor: "rgb(255, 255, 0)",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24
|
// borderRadius: 24
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
selected: {}
|
// selected: {}
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
// const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
||||||
grouped: {
|
// grouped: {
|
||||||
margin: theme.spacing(-0.5),
|
// margin: theme.spacing(-0.5),
|
||||||
border: "none",
|
// border: "none",
|
||||||
padding: theme.spacing(1),
|
// padding: theme.spacing(1),
|
||||||
"&:not(:first-child):not(:last-child)": {
|
// "&:not(:first-child):not(:last-child)": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.5),
|
// marginRight: theme.spacing(0.5),
|
||||||
marginLeft: theme.spacing(0.5)
|
// marginLeft: theme.spacing(0.5)
|
||||||
},
|
// },
|
||||||
"&:first-child": {
|
// "&:first-child": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginLeft: theme.spacing(0.25)
|
// marginLeft: theme.spacing(0.25)
|
||||||
},
|
// },
|
||||||
"&:last-child": {
|
// "&:last-child": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.25)
|
// marginRight: theme.spacing(0.25)
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: darken(
|
// backgroundColor: darken(
|
||||||
theme.palette.background.paper,
|
// theme.palette.background.paper,
|
||||||
getThemeType() === "light" ? 0.05 : 0.25
|
// getThemeType() === "light" ? 0.05 : 0.25
|
||||||
),
|
// ),
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
content: "border-box"
|
// content: "border-box"
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModeTime(moment(bin.status.lastModeChange));
|
setModeTime(moment(bin.status.lastModeChange));
|
||||||
|
|
@ -1705,6 +1706,18 @@ export default function BinVisualizer(props: Props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const determineToggle = (binMode: pond.BinMode) => {
|
||||||
|
switch (binMode) {
|
||||||
|
case pond.BinMode.BIN_MODE_STORAGE:
|
||||||
|
return [0]
|
||||||
|
case pond.BinMode.BIN_MODE_COOLDOWN:
|
||||||
|
return [1]
|
||||||
|
case pond.BinMode.BIN_MODE_DRYING:
|
||||||
|
case pond.BinMode.BIN_MODE_HYDRATING:
|
||||||
|
return [2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const binMode = () => {
|
const binMode = () => {
|
||||||
const mode = bin.settings.mode;
|
const mode = bin.settings.mode;
|
||||||
return (
|
return (
|
||||||
|
|
@ -1720,7 +1733,31 @@ export default function BinVisualizer(props: Props) {
|
||||||
<Typography variant="subtitle1" style={{ fontWeight: 800 }}>
|
<Typography variant="subtitle1" style={{ fontWeight: 800 }}>
|
||||||
Bin Mode
|
Bin Mode
|
||||||
</Typography>
|
</Typography>
|
||||||
<StyledToggleButtonGroup
|
<ButtonGroup
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Storage",
|
||||||
|
function: () => {
|
||||||
|
setModeStorage()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cooldown",
|
||||||
|
function: () => {
|
||||||
|
setModeCooldown()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: bin.settings.inventory && bin.settings.inventory?.initialMoisture < bin.settings.inventory?.targetMoisture ? "Hydrating" : "Drying",
|
||||||
|
function: () => {
|
||||||
|
setShowInputMoisture(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
toggledButtons={determineToggle(mode)}
|
||||||
|
toggle
|
||||||
|
/>
|
||||||
|
{/* <StyledToggleButtonGroup
|
||||||
id="tour-bin-mode"
|
id="tour-bin-mode"
|
||||||
value={mode}
|
value={mode}
|
||||||
exclusive
|
exclusive
|
||||||
|
|
@ -1754,7 +1791,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
Drying
|
Drying
|
||||||
</StyledToggle>
|
</StyledToggle>
|
||||||
)}
|
)}
|
||||||
</StyledToggleButtonGroup>
|
</StyledToggleButtonGroup> */}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -2067,6 +2104,7 @@ export default function BinVisualizer(props: Props) {
|
||||||
devices={devices}
|
devices={devices}
|
||||||
refreshCallback={success => {
|
refreshCallback={success => {
|
||||||
setShowInputMoisture(false);
|
setShowInputMoisture(false);
|
||||||
|
setNewPreset(0)
|
||||||
if (success) updateBin();
|
if (success) updateBin();
|
||||||
}}
|
}}
|
||||||
parentPreset={newPreset}
|
parentPreset={newPreset}
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
borderTopLeftRadius: "6px",
|
borderTopLeftRadius: "6px",
|
||||||
borderTopRightRadius: "6px",
|
borderTopRightRadius: "6px",
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
|
fontWeight: 1000,
|
||||||
transform: "0.3s ease-in-out",
|
transform: "0.3s ease-in-out",
|
||||||
// "linear-gradient(rgba(150, 150, 150, 0.3),"
|
// "linear-gradient(rgba(150, 150, 150, 0.3),"
|
||||||
// + theme.palette.background.default + " 80%)"
|
// + theme.palette.background.default + " 80%)"
|
||||||
|
|
|
||||||
|
|
@ -123,10 +123,11 @@ export default function BinyardDisplay(props: Props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadBins = useCallback(() => {
|
const loadBins = useCallback(() => {
|
||||||
setBinsLoading(true);
|
if (yardKey === "") return
|
||||||
//load the bins based on the yard key
|
//load the bins based on the yard key
|
||||||
|
setBinsLoading(true);
|
||||||
binAPI
|
binAPI
|
||||||
.listBinsAndData(maxBins, 0, "asc", "name", undefined, as, undefined, [yardKey], ["binyard"])
|
.listBinsAndData(maxBins, 0, "asc", "name", undefined, undefined, undefined, [yardKey], ["binyard"])
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
let yardBins: Bin[] = [];
|
let yardBins: Bin[] = [];
|
||||||
let grainBins: Bin[] = [];
|
let grainBins: Bin[] = [];
|
||||||
|
|
@ -315,17 +316,10 @@ export default function BinyardDisplay(props: Props) {
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
<BinsList
|
<BinsList
|
||||||
gridView
|
gridView
|
||||||
|
valDisplay="average"
|
||||||
bins={grainBins}
|
bins={grainBins}
|
||||||
duplicateBin={duplicateBin}
|
duplicateBin={duplicateBin}
|
||||||
title={"Grain Bins"}
|
title={"Grain Bins"}
|
||||||
// because this only triggers in scroll view it is no longer necessary as we dont do the scroll here
|
|
||||||
// loadMore={newTranslation => {
|
|
||||||
// //only triggered in the scroll view so this will never trigger in grid view
|
|
||||||
// if (yardBins.length < binTotal) {
|
|
||||||
// loadMoreBins(contentFilter);
|
|
||||||
// setScrollTranslations({ ...scrollTranslations, bins: newTranslation });
|
|
||||||
// }
|
|
||||||
// }}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
@ -338,16 +332,10 @@ export default function BinyardDisplay(props: Props) {
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
<BinsList
|
<BinsList
|
||||||
gridView
|
gridView
|
||||||
|
valDisplay="average"
|
||||||
bins={fertBins}
|
bins={fertBins}
|
||||||
duplicateBin={duplicateBin}
|
duplicateBin={duplicateBin}
|
||||||
title={"Fertilizer Bins"}
|
title={"Fertilizer Bins"}
|
||||||
// loadMore={newTranslation => {
|
|
||||||
// //only triggered in the scroll view so this will never trigger in grid view
|
|
||||||
// if (yardBins.length < binTotal) {
|
|
||||||
// loadMoreBins(contentFilter);
|
|
||||||
// setScrollTranslations({ ...scrollTranslations, fertilizer: newTranslation });
|
|
||||||
// }
|
|
||||||
// }}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
@ -360,6 +348,7 @@ export default function BinyardDisplay(props: Props) {
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
<BinsList
|
<BinsList
|
||||||
gridView
|
gridView
|
||||||
|
valDisplay="average"
|
||||||
bins={emptyBins}
|
bins={emptyBins}
|
||||||
duplicateBin={duplicateBin}
|
duplicateBin={duplicateBin}
|
||||||
title={"Empty Bins"}
|
title={"Empty Bins"}
|
||||||
|
|
|
||||||
161
src/common/ButtonGroup.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { Box, Button, darken, Theme, Typography } from "@mui/material";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { getSecondaryColour } from "services/whiteLabel";
|
||||||
|
import { getThemeType } from "theme/themeType";
|
||||||
|
|
||||||
|
export interface ButtonData {
|
||||||
|
/**
|
||||||
|
* The title and description of the button
|
||||||
|
*/
|
||||||
|
title: string
|
||||||
|
/**
|
||||||
|
* An icon that would replace the text of the button
|
||||||
|
*/
|
||||||
|
icon?: JSX.Element
|
||||||
|
/**
|
||||||
|
* The function to run when the button is clicked or toggled on when toggle is true
|
||||||
|
*/
|
||||||
|
function: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/**
|
||||||
|
* The array of button data to be rendered by the group
|
||||||
|
*/
|
||||||
|
buttons: ButtonData[]
|
||||||
|
/**
|
||||||
|
* When true will track the toggle state of the buttons, false will just be basic buttons
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
toggle?: boolean
|
||||||
|
/**
|
||||||
|
* When true will allow multiple buttons to be toggled, false will untoggle the previous button when another is selected
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
multiToggle?: boolean
|
||||||
|
/**
|
||||||
|
* Can be used to control which buttons are toggled on from the parent by passing the indexes of the buttons to toggle on.
|
||||||
|
* If not passed in will default to toggle the first button and then be controlled internally
|
||||||
|
*
|
||||||
|
* @default undefined
|
||||||
|
*/
|
||||||
|
toggledButtons?: number[]
|
||||||
|
/**
|
||||||
|
* Numerical value for the size of the text for buttons that dont use the icon
|
||||||
|
*/
|
||||||
|
textSize?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
return ({
|
||||||
|
container: {
|
||||||
|
backgroundColor: darken(
|
||||||
|
theme.palette.background.paper,
|
||||||
|
getThemeType() === "light" ? 0.10 : 0.05
|
||||||
|
),
|
||||||
|
borderRadius: 24,
|
||||||
|
content: "border-box",
|
||||||
|
border: "none",
|
||||||
|
//padding: theme.spacing(1),
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
overflow: "visible",
|
||||||
|
content: "content-box",
|
||||||
|
borderRadius: 24,
|
||||||
|
marginLeft: theme.spacing(0.05),
|
||||||
|
marginRight: theme.spacing(0.05),
|
||||||
|
"&:hover": {
|
||||||
|
//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", //for now Dustin just wants it to be gold
|
||||||
|
color: "black",
|
||||||
|
borderRadius: 24,
|
||||||
|
fontWeight: "bold"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function ButtonGroup(props: Props){
|
||||||
|
const { buttons, toggle, toggledButtons, multiToggle, textSize } = props
|
||||||
|
const classes = useStyles()
|
||||||
|
const [currentToggle, setCurrentToggle] = useState<number[]>([])
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
//determine the default state of the button toggles if toggle is true
|
||||||
|
if(toggle){
|
||||||
|
let toggled: number [] = [0]
|
||||||
|
if(toggledButtons){
|
||||||
|
toggled = toggledButtons
|
||||||
|
}
|
||||||
|
setCurrentToggle(toggled)
|
||||||
|
}
|
||||||
|
},[toggle, toggledButtons])
|
||||||
|
|
||||||
|
const checkToggled = (index: number) => {
|
||||||
|
return currentToggle.includes(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleControl = (i: number) => {
|
||||||
|
let toggled = cloneDeep(currentToggle)
|
||||||
|
//is the button currently toggled
|
||||||
|
if(checkToggled(i)){
|
||||||
|
//if the button is toggled
|
||||||
|
if(multiToggle){
|
||||||
|
//un-toggle the button (remove it from the array)
|
||||||
|
console.log(toggled)
|
||||||
|
toggled.splice(currentToggle.indexOf(i), 1)
|
||||||
|
}
|
||||||
|
//and if it is single toggle do nothing
|
||||||
|
} else {
|
||||||
|
//if multiToggle is false
|
||||||
|
if(!multiToggle){
|
||||||
|
//replace the currentToggle array
|
||||||
|
toggled = [i]
|
||||||
|
}else{
|
||||||
|
//add it to the currentToggle array
|
||||||
|
toggled.push(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCurrentToggle(toggled)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className={classes.container} display="flex" flexDirection="row">
|
||||||
|
{buttons.map((b, i) => (
|
||||||
|
<Button
|
||||||
|
key={i}
|
||||||
|
className={checkToggled(i) ? classes.buttonToggled : classes.button}
|
||||||
|
onClick={() => {
|
||||||
|
if(toggle){
|
||||||
|
//control the toggle internally if the array was not passed in
|
||||||
|
if(!toggledButtons){
|
||||||
|
toggleControl(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.function()
|
||||||
|
}}>
|
||||||
|
{b.icon
|
||||||
|
?
|
||||||
|
b.icon
|
||||||
|
:
|
||||||
|
<Typography sx={{fontSize: textSize ?? 13, fontWeight: checkToggled(i) ? 650 : 500}}>
|
||||||
|
{b.title}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ export default function DisplayDrawer(props: Props) {
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography align="center" variant="h5">
|
<Typography sx={{ color: "black", fontWeight: 650}} align="center" variant="h5">
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Beenhere } from "@mui/icons-material";
|
||||||
import { useThemeType } from "hooks";
|
import { useThemeType } from "hooks";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Map, { Marker } from "react-map-gl/mapbox-legacy";
|
import Map, { Marker } from "react-map-gl/mapbox";
|
||||||
|
|
||||||
const MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
|
const MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,12 @@ import {
|
||||||
LinkProps,
|
LinkProps,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Theme,
|
Theme,
|
||||||
|
Typography,
|
||||||
useTheme
|
useTheme
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Replay } from "@mui/icons-material";
|
import { Replay } from "@mui/icons-material";
|
||||||
import { useMobile } from "hooks";
|
import { useMobile } from "hooks";
|
||||||
import { ReactNode, useEffect, useRef } from "react";
|
import { ReactNode } from "react";
|
||||||
import { useLocation } from "react-router";
|
import { useLocation } from "react-router";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import { or } from "utils/types";
|
import { or } from "utils/types";
|
||||||
|
|
@ -57,11 +58,13 @@ interface Props {
|
||||||
paddingTop?: number;
|
paddingTop?: number;
|
||||||
paddingBottom?: number;
|
paddingBottom?: number;
|
||||||
prependPaths?: string[];
|
prependPaths?: string[];
|
||||||
|
state?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LinkRouterProps extends LinkProps {
|
interface LinkRouterProps extends LinkProps {
|
||||||
to: string;
|
to: string;
|
||||||
replace?: boolean;
|
replace?: boolean;
|
||||||
|
state?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LinkRouter = (props: LinkRouterProps) => <Link {...props} component={RouterLink as any} />;
|
const LinkRouter = (props: LinkRouterProps) => <Link {...props} component={RouterLink as any} />;
|
||||||
|
|
@ -72,7 +75,7 @@ export default function SmartBreadcrumb(props: Props) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { loading, reload } = props;
|
const { loading, reload, state } = props;
|
||||||
|
|
||||||
const groupID = (): string => {
|
const groupID = (): string => {
|
||||||
let index = prependPaths?.indexOf("groups")
|
let index = prependPaths?.indexOf("groups")
|
||||||
|
|
@ -289,7 +292,7 @@ export default function SmartBreadcrumb(props: Props) {
|
||||||
|
|
||||||
if (prependPaths) pathnames.splice(pathnames.length - 1, 0, ...prependPaths);
|
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 lastPath = index === pathnames.length - 1;
|
||||||
const to = `/${pathnames.slice(0, index + 1).join("/")}`;
|
const to = `/${pathnames.slice(0, index + 1).join("/")}`;
|
||||||
|
|
@ -310,6 +313,21 @@ export default function SmartBreadcrumb(props: Props) {
|
||||||
links.push(linkComponent(to, label, lastPath));
|
links.push(linkComponent(to, label, lastPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value === "support") {
|
||||||
|
links.push(
|
||||||
|
<Chip
|
||||||
|
key={"support-chip"}
|
||||||
|
variant={"filled"}
|
||||||
|
label={
|
||||||
|
<Typography variant="subtitle1" color="textPrimary">
|
||||||
|
Support
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
className={classes.chip}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return links;
|
return links;
|
||||||
};
|
};
|
||||||
|
|
@ -324,6 +342,7 @@ export default function SmartBreadcrumb(props: Props) {
|
||||||
color={lastPath ? "textPrimary" : "textSecondary"}
|
color={lastPath ? "textPrimary" : "textSecondary"}
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
to={to}
|
to={to}
|
||||||
|
state={state}
|
||||||
sx={{ '&:hover': { color: getThemeType() === "dark" ? 'white' : "black" }}}
|
sx={{ '&:hover': { color: getThemeType() === "dark" ? 'white' : "black" }}}
|
||||||
className={classes.textOverflow}>
|
className={classes.textOverflow}>
|
||||||
{label}
|
{label}
|
||||||
|
|
|
||||||
|
|
@ -176,12 +176,12 @@ export default function StatusPlenum(props: Props) {
|
||||||
>
|
>
|
||||||
<Grid2>
|
<Grid2>
|
||||||
<Typography variant="body2" style={{ color: blue[300]}}>
|
<Typography variant="body2" style={{ color: blue[300]}}>
|
||||||
{device.status.sen5x?.voc.toFixed(1)}%
|
V: {device.status.sen5x?.voc.toFixed(1)}%
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<Grid2>
|
<Grid2>
|
||||||
<Typography variant="body2" style={{ color: blue[400]}}>
|
<Typography variant="body2" style={{ color: blue[400]}}>
|
||||||
{device.status.sen5x?.nox.toFixed(1)}%
|
N: {device.status.sen5x?.nox.toFixed(1)}%
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import {
|
||||||
Button,
|
Button,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
Grid,
|
Grid2 as Grid,
|
||||||
|
TextField,
|
||||||
Theme,
|
Theme,
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
@ -13,6 +14,8 @@ import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
import { DateRangePreset, GetDefaultDateRange, SetDefaultPreset } from "./DateRange";
|
import { DateRangePreset, GetDefaultDateRange, SetDefaultPreset } from "./DateRange";
|
||||||
import { useThemeType } from "hooks";
|
import { useThemeType } from "hooks";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { color } from "@mui/system";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
startDate: Moment;
|
startDate: Moment;
|
||||||
|
|
@ -26,10 +29,12 @@ type DateRange<T> = [T | null, T | null];
|
||||||
const useStyles = makeStyles((_theme: Theme) => {
|
const useStyles = makeStyles((_theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
activeButtonLight: {
|
activeButtonLight: {
|
||||||
border: "1px solid black"
|
border: "1px solid black",
|
||||||
|
color: "black"
|
||||||
},
|
},
|
||||||
acitveButtonDark: {
|
acitveButtonDark: {
|
||||||
border: "1px solid white"
|
border: "1px solid white",
|
||||||
|
color: "white"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
@ -49,7 +54,7 @@ export default function TimeBar(props: Props) {
|
||||||
|
|
||||||
const submitDateRange = () => {
|
const submitDateRange = () => {
|
||||||
const startDate = dateRange[0] ? dateRange[0].clone() : moment();
|
const startDate = dateRange[0] ? dateRange[0].clone() : moment();
|
||||||
const endDate = dateRange[1] ? dateRange[1].clone() : moment();
|
const endDate = dateRange[1] ? dateRange[1].add(1, "day").clone() : moment(); //add 1 day to the end time to include that day rather than have it be the cutoff
|
||||||
updateDateRange(startDate, endDate, false);
|
updateDateRange(startDate, endDate, false);
|
||||||
setDateRangeDialog(false);
|
setDateRangeDialog(false);
|
||||||
setActiveView("selectRange");
|
setActiveView("selectRange");
|
||||||
|
|
@ -85,19 +90,30 @@ export default function TimeBar(props: Props) {
|
||||||
onClose={() => setDateRangeDialog(false)}
|
onClose={() => setDateRangeDialog(false)}
|
||||||
aria-labelledby="date-range-dialog">
|
aria-labelledby="date-range-dialog">
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
{/* <StaticDateRangePicker
|
<TextField
|
||||||
disableFuture
|
fullWidth
|
||||||
value={dateRange}
|
margin="normal"
|
||||||
onChange={date => setDateRange(date)}
|
type="date"
|
||||||
renderInput={(startProps, endProps) => (
|
label="Start Time"
|
||||||
<React.Fragment>
|
value={dateRange[0]?.format("YYYY-MM-DD")}
|
||||||
<TextField {...startProps} />
|
onChange={e => {
|
||||||
<DateRangeDelimiter> to </DateRangeDelimiter>
|
let range = cloneDeep(dateRange)
|
||||||
<TextField {...endProps} />
|
range[0] = moment(e.target.value)
|
||||||
</React.Fragment>
|
setDateRange(range)
|
||||||
)}
|
}}
|
||||||
/> */}
|
/>
|
||||||
DATE PICKER GOES HERE
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
type="date"
|
||||||
|
label="End Time"
|
||||||
|
value={dateRange[1]?.format("YYYY-MM-DD")}
|
||||||
|
onChange={e => {
|
||||||
|
let range = cloneDeep(dateRange)
|
||||||
|
range[1] = moment(e.target.value)
|
||||||
|
setDateRange(range)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => setDateRangeDialog(false)} color="primary">
|
<Button onClick={() => setDateRangeDialog(false)} color="primary">
|
||||||
|
|
@ -115,7 +131,7 @@ export default function TimeBar(props: Props) {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{datePickerDialog()}
|
{datePickerDialog()}
|
||||||
<Grid container direction="row" spacing={1} wrap="nowrap">
|
<Grid container direction="row" spacing={1} wrap="nowrap">
|
||||||
<Grid item>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
style={{ borderRadius: "25px" }}
|
style={{ borderRadius: "25px" }}
|
||||||
className={
|
className={
|
||||||
|
|
@ -132,7 +148,7 @@ export default function TimeBar(props: Props) {
|
||||||
<Typography style={{ fontWeight: 650 }}>1H</Typography>
|
<Typography style={{ fontWeight: 650 }}>1H</Typography>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
style={{ borderRadius: "25px" }}
|
style={{ borderRadius: "25px" }}
|
||||||
className={
|
className={
|
||||||
|
|
@ -149,7 +165,7 @@ export default function TimeBar(props: Props) {
|
||||||
<Typography style={{ fontWeight: 650 }}>1D</Typography>
|
<Typography style={{ fontWeight: 650 }}>1D</Typography>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
style={{ borderRadius: "25px" }}
|
style={{ borderRadius: "25px" }}
|
||||||
className={
|
className={
|
||||||
|
|
@ -166,7 +182,7 @@ export default function TimeBar(props: Props) {
|
||||||
<Typography style={{ fontWeight: 650 }}>1W</Typography>
|
<Typography style={{ fontWeight: 650 }}>1W</Typography>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
style={{ borderRadius: "25px" }}
|
style={{ borderRadius: "25px" }}
|
||||||
className={
|
className={
|
||||||
|
|
@ -183,7 +199,7 @@ export default function TimeBar(props: Props) {
|
||||||
<Typography style={{ fontWeight: 650 }}>1M</Typography>
|
<Typography style={{ fontWeight: 650 }}>1M</Typography>
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
style={{ borderRadius: "25px" }}
|
style={{ borderRadius: "25px" }}
|
||||||
className={
|
className={
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,10 @@ export default function ComponentActions(props: Props) {
|
||||||
onClose={closeMoreMenu}
|
onClose={closeMoreMenu}
|
||||||
disableAutoFocusItem>
|
disableAutoFocusItem>
|
||||||
{canAddInteraction && (
|
{canAddInteraction && (
|
||||||
<MenuItem dense onClick={() => setIsAddInteractionOpen(true)} divider>
|
<MenuItem dense onClick={() => {
|
||||||
|
setIsAddInteractionOpen(true)
|
||||||
|
closeMoreMenu()
|
||||||
|
}} divider>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<AddCircle className={classes.addIcon} />
|
<AddCircle className={classes.addIcon} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|
@ -126,6 +129,7 @@ export default function ComponentActions(props: Props) {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsJSON(false);
|
setIsJSON(false);
|
||||||
setIsExportDataOpen(true);
|
setIsExportDataOpen(true);
|
||||||
|
closeMoreMenu()
|
||||||
}}>
|
}}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<SaveAlt className={classes.exportDataIcon} />
|
<SaveAlt className={classes.exportDataIcon} />
|
||||||
|
|
@ -134,7 +138,10 @@ export default function ComponentActions(props: Props) {
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{canEdit && <Divider />}
|
{canEdit && <Divider />}
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<MenuItem dense onClick={() => openComponentSettingsDialog("remove")}>
|
<MenuItem dense onClick={() => {
|
||||||
|
openComponentSettingsDialog("remove")
|
||||||
|
setAnchorEl(null);
|
||||||
|
}}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DeleteOutline className={classes.red} />
|
<DeleteOutline className={classes.red} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|
|
||||||
|
|
@ -254,15 +254,15 @@ export default function ComponentCard(props: Props) {
|
||||||
size="small"
|
size="small"
|
||||||
onTouchStart={event => {
|
onTouchStart={event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
//event.preventDefault();
|
||||||
}}
|
}}
|
||||||
onMouseDown={event => {
|
onMouseDown={event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
//event.preventDefault();
|
||||||
}}
|
}}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
//event.preventDefault();
|
||||||
}}
|
}}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
onChange={(_, checked) => updateControllerState(checked)}
|
onChange={(_, checked) => updateControllerState(checked)}
|
||||||
|
|
|
||||||
|
|
@ -237,8 +237,7 @@ export default function ComponentForm(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let comp = tidyComponent(form.component);
|
componentChanged(form.component, isFormValid());
|
||||||
componentChanged(comp, isFormValid());
|
|
||||||
}, [form.component, componentChanged]); // eslint-disable-line react-hooks/exhaustive-deps
|
}, [form.component, componentChanged]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
const isNameValid = (name: string): boolean => {
|
const isNameValid = (name: string): boolean => {
|
||||||
|
|
@ -950,7 +949,7 @@ export default function ComponentForm(props: Props) {
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={
|
control={
|
||||||
<Switch
|
<Switch
|
||||||
checked={measure}
|
checked={measure}
|
||||||
onChange={toggleMeasure}
|
onChange={toggleMeasure}
|
||||||
name="measure"
|
name="measure"
|
||||||
aria-label="measure"
|
aria-label="measure"
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ import {
|
||||||
AccountCircle as ObjectUsersIcon,
|
AccountCircle as ObjectUsersIcon,
|
||||||
SupervisedUserCircle as ObjectTeamsIcon,
|
SupervisedUserCircle as ObjectTeamsIcon,
|
||||||
Sync as SyncDeviceIcon,
|
Sync as SyncDeviceIcon,
|
||||||
Wifi as WifiIcon
|
Wifi as WifiIcon,
|
||||||
|
Visibility
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { Skeleton } from "@mui/material";
|
import { Skeleton } from "@mui/material";
|
||||||
// import Datadog from "assets/external/datadog.png";
|
// import Datadog from "assets/external/datadog.png";
|
||||||
|
|
@ -47,7 +48,7 @@ import { Component, Device, deviceScope, Interaction } from "models";
|
||||||
// import { isShareableLink } from "pbHelpers/Device";
|
// import { isShareableLink } from "pbHelpers/Device";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { useGlobalState } from "providers";
|
import { useGlobalState } from "providers";
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
// import { useHistory, useRouteMatch } from "react-router";
|
// import { useHistory, useRouteMatch } from "react-router";
|
||||||
import { isOffline } from "utils/environment";
|
import { isOffline } from "utils/environment";
|
||||||
import { or } from "utils/types";
|
import { or } from "utils/types";
|
||||||
|
|
@ -70,6 +71,8 @@ import SaveDeviceProfile from "./SaveDeviceProfile";
|
||||||
import Connection from "./Connection";
|
import Connection from "./Connection";
|
||||||
import ComponentOrder from "component/ComponentOrder";
|
import ComponentOrder from "component/ComponentOrder";
|
||||||
import ArcGISDeviceData from "./ArcGISDeviceData";
|
import ArcGISDeviceData from "./ArcGISDeviceData";
|
||||||
|
import UpgradeDevice from "./UpgradeDevice";
|
||||||
|
import { DevicePageData } from "pages/Device";
|
||||||
|
|
||||||
const useStyles = makeStyles((_theme: Theme) => {
|
const useStyles = makeStyles((_theme: Theme) => {
|
||||||
// const isMobile = useMobile()
|
// const isMobile = useMobile()
|
||||||
|
|
@ -168,6 +171,19 @@ export default function DeviceActions(props: Props) {
|
||||||
isJsonDataDialogOpen: false
|
isJsonDataDialogOpen: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [devicePageData, setDevicePageData] = useState<DevicePageData | undefined>(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) => {
|
const openDialog = (target: keyof DialogState) => {
|
||||||
let updatedDialogState = cloneDeep(dialogState);
|
let updatedDialogState = cloneDeep(dialogState);
|
||||||
updatedDialogState[target] = true;
|
updatedDialogState[target] = true;
|
||||||
|
|
@ -494,10 +510,19 @@ export default function DeviceActions(props: Props) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showSupport = () => {
|
||||||
|
return user.allowedTo("provision") && !location.pathname.includes("support")
|
||||||
|
}
|
||||||
|
|
||||||
const buttons = () => {
|
const buttons = () => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{/* {canWrite && user.allowedTo("provision") && <UpgradeDevice device={device} />} */}
|
{showSupport() && <Tooltip title="Support">
|
||||||
|
<IconButton onClick={() => navigate("support", { state: {device: device, devicePageData: devicePageData }})}>
|
||||||
|
<Visibility />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>}
|
||||||
|
{canWrite && user.allowedTo("provision") && <UpgradeDevice device={device} />}
|
||||||
{/* {!isShareableLink(match.params.deviceID) && ( */}
|
{/* {!isShareableLink(match.params.deviceID) && ( */}
|
||||||
<NotificationButton
|
<NotificationButton
|
||||||
notify={preferences.notify}
|
notify={preferences.notify}
|
||||||
|
|
|
||||||
321
src/device/UpgradeDevice.tsx
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
LinearProgress,
|
||||||
|
Link,
|
||||||
|
Theme,
|
||||||
|
Tooltip,
|
||||||
|
Typography
|
||||||
|
} from "@mui/material";
|
||||||
|
// import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
|
import UpdateIcon from "@mui/icons-material/Update";
|
||||||
|
import { useDeviceAPI, useFirmwareAPI, usePrevious, useSnackbar } from "hooks";
|
||||||
|
import { Device, latestFirmwareVersion, Upgrade } from "models";
|
||||||
|
import moment from "moment";
|
||||||
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
||||||
|
import { useGlobalState } from "providers";
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
import { or } from "utils/types";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
return ({
|
||||||
|
dialogContent: {
|
||||||
|
marginTop: theme.spacing(2)
|
||||||
|
},
|
||||||
|
buttonProgress: {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: 1
|
||||||
|
},
|
||||||
|
failed: {
|
||||||
|
color: theme.palette.error.main
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
device: Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UpgradeDevice(props: Props) {
|
||||||
|
const classes = useStyles();
|
||||||
|
const deviceAPI = useDeviceAPI();
|
||||||
|
const { success, error } = useSnackbar();
|
||||||
|
const firmwareAPI = useFirmwareAPI();
|
||||||
|
const [{ firmware }] = useGlobalState();
|
||||||
|
const prevFirmware = usePrevious(firmware);
|
||||||
|
const { device } = props;
|
||||||
|
const prevDevice = usePrevious(device);
|
||||||
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
const prevOpen = usePrevious(open);
|
||||||
|
const [upgrade, setUpgrade] = useState<Upgrade>(Upgrade.create());
|
||||||
|
const [gettingStatus, setGettingStatus] = useState(0);
|
||||||
|
|
||||||
|
const progress = useCallback(() => {
|
||||||
|
return upgrade.status.progress;
|
||||||
|
}, [upgrade.status.progress]);
|
||||||
|
|
||||||
|
const currentFirmware = () => {
|
||||||
|
return device.status.firmwareVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
const latestFirmware = () => {
|
||||||
|
return latestFirmwareVersion(
|
||||||
|
firmware,
|
||||||
|
device.settings.platform,
|
||||||
|
device.settings.upgradeChannel
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const upgradeVersion = () => {
|
||||||
|
return upgrade.settings.version;
|
||||||
|
};
|
||||||
|
|
||||||
|
const available = () => {
|
||||||
|
return currentFirmware() !== latestFirmware();
|
||||||
|
};
|
||||||
|
|
||||||
|
const upgrading = useCallback(() => {
|
||||||
|
return progress() === "upgrading";
|
||||||
|
}, [progress]);
|
||||||
|
|
||||||
|
const completed = () => {
|
||||||
|
return progress() === "completed" && currentFirmware() === latestFirmware();
|
||||||
|
};
|
||||||
|
const failed = () => {
|
||||||
|
return progress() === "failed" && upgradeVersion() === latestFirmware();
|
||||||
|
};
|
||||||
|
|
||||||
|
const percent = () => {
|
||||||
|
return Math.round((upgrade.status.offset / upgrade.status.size) * 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tooltip = () => {
|
||||||
|
return upgrading()
|
||||||
|
? "Upgrade in progress: " + percent() + "%"
|
||||||
|
: failed()
|
||||||
|
? "Upgrade failed"
|
||||||
|
: "Upgrade Device";
|
||||||
|
};
|
||||||
|
|
||||||
|
const upgradeTimingDesc = () => {
|
||||||
|
let desc = "requested " + moment(or(upgrade.status.started, 0)).fromNow();
|
||||||
|
if (completed()) {
|
||||||
|
desc = "completed " + moment(or(upgrade.status.stopped, 0)).fromNow();
|
||||||
|
} else if (failed()) {
|
||||||
|
desc = "failed " + moment(or(upgrade.status.changed, 0)).fromNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mode = () => {
|
||||||
|
let mode = "prompt";
|
||||||
|
if (upgrading() || failed() || completed()) {
|
||||||
|
mode = "progress";
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatus = useCallback(() => {
|
||||||
|
if (gettingStatus === device.id() && !upgrading()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setGettingStatus(device.id());
|
||||||
|
deviceAPI
|
||||||
|
.getUpgradeStatus(device.id(), getContextKeys(), getContextTypes())
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res && res.data && res.data.upgrade) {
|
||||||
|
setUpgrade(Upgrade.any(res.data.upgrade));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => error(err));
|
||||||
|
}, [device, deviceAPI, error, gettingStatus, upgrading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const deviceIdUnchanged = !prevDevice || prevDevice.id() === device.id();
|
||||||
|
if (
|
||||||
|
(open && !prevOpen) ||
|
||||||
|
firmware !== prevFirmware ||
|
||||||
|
(device !== prevDevice && deviceIdUnchanged)
|
||||||
|
) {
|
||||||
|
getStatus();
|
||||||
|
}
|
||||||
|
}, [open, prevOpen, getStatus, device, prevDevice, firmware, prevFirmware]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function getStatusAgain() {
|
||||||
|
if (upgrading()) {
|
||||||
|
getStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const interval = setInterval(() => getStatusAgain(), 5000);
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, [open, getStatus, upgrading]);
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpgrade = () => {
|
||||||
|
firmwareAPI
|
||||||
|
.upgradeFirmware(device.id())
|
||||||
|
.then(() => {
|
||||||
|
success(device.name() + " will upgrade to " + latestFirmware() + " next time it checks in");
|
||||||
|
getStatus();
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.error(err);
|
||||||
|
error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const restart = () => {
|
||||||
|
firmwareAPI
|
||||||
|
.upgradeFirmware(device.id())
|
||||||
|
.then(() => {
|
||||||
|
success(
|
||||||
|
"Upgrade successfully restarted, " +
|
||||||
|
device.name() +
|
||||||
|
" will upgrade next time it checks in"
|
||||||
|
);
|
||||||
|
getStatus();
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.error(err);
|
||||||
|
error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
firmwareAPI
|
||||||
|
.cancelUpgrade(device.id())
|
||||||
|
.then(() => {
|
||||||
|
success("Upgrade cancelled");
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.error(err);
|
||||||
|
error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const button = () => {
|
||||||
|
return (
|
||||||
|
<Tooltip title={tooltip()}>
|
||||||
|
<IconButton
|
||||||
|
color="default"
|
||||||
|
className={failed() ? classes.failed : undefined}
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
aria-label={tooltip()}>
|
||||||
|
<UpdateIcon />
|
||||||
|
{upgrading() && (
|
||||||
|
<CircularProgress
|
||||||
|
// variant="static"
|
||||||
|
value={percent()}
|
||||||
|
size={"2rem"}
|
||||||
|
className={classes.buttonProgress}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{(available() || upgrading() || failed()) && button()}
|
||||||
|
<Dialog fullWidth open={open} onClose={close} aria-labelledby="form-dialog-title">
|
||||||
|
{mode() === "prompt" && (
|
||||||
|
<React.Fragment>
|
||||||
|
<DialogTitle id="form-dialog-title">
|
||||||
|
Upgrade Device
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
{device.name()}
|
||||||
|
</Typography>
|
||||||
|
</DialogTitle>
|
||||||
|
<Divider />
|
||||||
|
<DialogContent className={classes.dialogContent}>
|
||||||
|
<DialogContentText id="alert-dialog-slide-description">
|
||||||
|
Upgrading from {currentFirmware()} to {latestFirmware()}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
It is recommended that the device be plugged in during the upgrade as it will take
|
||||||
|
several minutes to complete
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
While the upgrade is in progress you can return to this dialog to view its status
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={close} color="primary">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleUpgrade} color="primary">
|
||||||
|
Upgrade
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
{mode() === "progress" && (
|
||||||
|
<React.Fragment>
|
||||||
|
<DialogTitle id="form-dialog-title">
|
||||||
|
Upgrading Device
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
{device.name()}
|
||||||
|
</Typography>
|
||||||
|
</DialogTitle>
|
||||||
|
<Divider />
|
||||||
|
<DialogContent className={classes.dialogContent}>
|
||||||
|
<DialogContentText id="alert-dialog-slide-description">
|
||||||
|
Upgrade to {latestFirmware()} {upgradeTimingDesc()}
|
||||||
|
</DialogContentText>
|
||||||
|
<br />
|
||||||
|
<LinearProgress variant="determinate" value={percent()} />
|
||||||
|
<DialogContentText>
|
||||||
|
{or(upgrade.status.offset, 0)} / {upgrade.status.size} bytes ({percent()}%)
|
||||||
|
</DialogContentText>
|
||||||
|
{failed() && (
|
||||||
|
<React.Fragment>
|
||||||
|
<DialogContentText>
|
||||||
|
<br />
|
||||||
|
Uh oh – something went wrong. You can try to{" "}
|
||||||
|
<Link variant="body2" onClick={restart} style={{ cursor: "pointer" }}>
|
||||||
|
restart
|
||||||
|
</Link>{" "}
|
||||||
|
the upgrade, if the problem persists we'll be alerted and follow up as soon as
|
||||||
|
possible.
|
||||||
|
</DialogContentText>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={close} color="primary">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
{upgrading() && (
|
||||||
|
<Button onClick={cancel} style={{ color: "red" }}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{failed() && (
|
||||||
|
<Button onClick={restart} color="primary">
|
||||||
|
Restart
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</DialogActions>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import GateSVG from "./GateSVG";
|
||||||
import GateGraphs from "./GateGraphs";
|
import GateGraphs from "./GateGraphs";
|
||||||
//import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab";
|
//import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab";
|
||||||
import { getThemeType } from "theme";
|
import { getThemeType } from "theme";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
gate: Gate;
|
gate: Gate;
|
||||||
|
|
@ -30,6 +31,8 @@ interface Props {
|
||||||
drawerView?: boolean;
|
drawerView?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function GateDevice(props: Props) {
|
export default function GateDevice(props: Props) {
|
||||||
const { gate, comprehensiveDevice, linkedCompList, drawerView } = props;
|
const { gate, comprehensiveDevice, linkedCompList, drawerView } = props;
|
||||||
const [{as}] = useGlobalState();
|
const [{as}] = useGlobalState();
|
||||||
|
|
@ -233,6 +236,7 @@ export default function GateDevice(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
id="ambientSelect"
|
id="ambientSelect"
|
||||||
|
variant="standard"
|
||||||
value={ambientKey}
|
value={ambientKey}
|
||||||
style={{ fontSize: 8 }}
|
style={{ fontSize: 8 }}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
|
|
@ -265,6 +269,7 @@ export default function GateDevice(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
id="tempSelect"
|
id="tempSelect"
|
||||||
|
variant="standard"
|
||||||
value={tempKey}
|
value={tempKey}
|
||||||
style={{ fontSize: 8 }}
|
style={{ fontSize: 8 }}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
|
|
@ -299,6 +304,7 @@ export default function GateDevice(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
id="pressureSelect"
|
id="pressureSelect"
|
||||||
|
variant="standard"
|
||||||
value={pressureKey}
|
value={pressureKey}
|
||||||
style={{ fontSize: 8 }}
|
style={{ fontSize: 8 }}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
|
|
@ -334,9 +340,10 @@ export default function GateDevice(props: Props) {
|
||||||
container
|
container
|
||||||
direction="row"
|
direction="row"
|
||||||
alignContent="center"
|
alignContent="center"
|
||||||
|
width="100%"
|
||||||
//alignItems="center"
|
//alignItems="center"
|
||||||
>
|
>
|
||||||
<Grid size={{ sm: 12, lg: isMobile || drawerView ? 12 : 8}} style={{ padding: 10 }}>
|
<Grid size={{ sm: 12, lg: isMobile || drawerView ? 12 : 4}} style={{ padding: 10 }}>
|
||||||
<Box
|
<Box
|
||||||
paddingLeft={1}
|
paddingLeft={1}
|
||||||
display="flex"
|
display="flex"
|
||||||
|
|
@ -344,7 +351,20 @@ export default function GateDevice(props: Props) {
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom={2}>
|
marginBottom={2}>
|
||||||
<Typography style={{ fontWeight: 650, fontSize: 20 }}>{device.name()}</Typography>
|
<Typography style={{ fontWeight: 650, fontSize: 20 }}>{device.name()}</Typography>
|
||||||
<ToggleButtonGroup value={detail} exclusive size="small" aria-label="detail">
|
<ButtonGroup
|
||||||
|
toggle
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Analysis",
|
||||||
|
function: () => setDetail("analytics")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Sensors",
|
||||||
|
function: () => setDetail("sensors")
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/* <ToggleButtonGroup value={detail} exclusive size="small" aria-label="detail">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
onClick={() => setDetail("analytics")}
|
onClick={() => setDetail("analytics")}
|
||||||
value={"analytics"}
|
value={"analytics"}
|
||||||
|
|
@ -357,7 +377,7 @@ export default function GateDevice(props: Props) {
|
||||||
aria-label="Sensor Graphs">
|
aria-label="Sensor Graphs">
|
||||||
Sensors
|
Sensors
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup> */}
|
||||||
</Box>
|
</Box>
|
||||||
<Card raised>
|
<Card raised>
|
||||||
<GateSVG
|
<GateSVG
|
||||||
|
|
|
||||||
|
|
@ -355,13 +355,13 @@ export default function GateSVG(props: Props) {
|
||||||
{renderValues()}
|
{renderValues()}
|
||||||
</g>
|
</g>
|
||||||
<foreignObject height={20} width={120} x={10} y={83}>
|
<foreignObject height={20} width={120} x={10} y={83}>
|
||||||
<Box style={{ position: "fixed" }}>{ambientSelector()}</Box>
|
<Box sx={{ position: "fixed" }}>{ambientSelector()}</Box>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
<foreignObject height={20} width={120} x={10} y={118}>
|
<foreignObject height={20} width={120} x={10} y={118}>
|
||||||
<Box style={{ position: "fixed" }}>{tempChainSelector()}</Box>
|
<Box sx={{ position: "fixed" }}>{tempChainSelector()}</Box>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
<foreignObject height={20} width={120} x={10} y={153}>
|
<foreignObject height={20} width={120} x={10} y={153}>
|
||||||
<Box style={{ position: "fixed" }}>{pressureChainSelector()}</Box>
|
<Box sx={{ position: "fixed" }}>{pressureChainSelector()}</Box>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
{pcaRed()}
|
{pcaRed()}
|
||||||
{pcaGreen()}
|
{pcaGreen()}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import { GeometryMapping, shapeFromCoords } from "models/GeometryMapping";
|
||||||
import { FeatureCollection, Feature } from "geojson";
|
import { FeatureCollection, Feature } from "geojson";
|
||||||
import DrawController from "./mapControllers/drawController";
|
import DrawController from "./mapControllers/drawController";
|
||||||
//import { Geometry } from "geojson";
|
//import { Geometry } from "geojson";
|
||||||
import Map, { MapRef, Marker, MarkerDragEvent } from "react-map-gl/mapbox-legacy";
|
import Map, { MapRef, Marker, MarkerDragEvent } from "react-map-gl/mapbox";
|
||||||
import { getDistanceUnit } from "utils";
|
import { getDistanceUnit } from "utils";
|
||||||
import { MapMouseEvent } from "mapbox-gl";
|
import { MapMouseEvent } from "mapbox-gl";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ControlPosition, useMap } from "react-map-gl/mapbox-legacy";
|
import { ControlPosition, useMap } from "react-map-gl/mapbox";
|
||||||
import MapboxGeocoder, { Result } from "@mapbox/mapbox-gl-geocoder";
|
import MapboxGeocoder, { Result } from "@mapbox/mapbox-gl-geocoder";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { useMap } from "react-map-gl/mapbox-legacy";
|
import { useMap } from "react-map-gl/mapbox";
|
||||||
import { FeatureCollection } from "geojson";
|
import { FeatureCollection } from "geojson";
|
||||||
import MapboxDraw from "@mapbox/mapbox-gl-draw";
|
import MapboxDraw from "@mapbox/mapbox-gl-draw";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Layer, Source } from "react-map-gl/mapbox-legacy";
|
import { Layer, Source } from "react-map-gl/mapbox";
|
||||||
import { FeatureCollection } from "geojson";
|
import { FeatureCollection } from "geojson";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { useMobile } from "hooks";
|
||||||
import { clone } from "lodash";
|
import { clone } from "lodash";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { Marker } from "react-map-gl/mapbox-legacy";
|
import { Marker } from "react-map-gl/mapbox";
|
||||||
|
|
||||||
//interface for markers
|
//interface for markers
|
||||||
export interface MarkerData {
|
export interface MarkerData {
|
||||||
|
|
|
||||||
|
|
@ -703,7 +703,12 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={3}>
|
<Grid size={3}>
|
||||||
<Button style={{ margin: "auto" }} onClick={() => setOpenFMSettings(true)}>
|
<Button
|
||||||
|
style={{ margin: "auto" }}
|
||||||
|
onClick={() => {
|
||||||
|
setOpenFMSettings(true)
|
||||||
|
setAnchorFieldMarker(null)
|
||||||
|
}}>
|
||||||
<Edit />
|
<Edit />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
32
src/models/Upgrade.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import { or } from "utils/types";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
|
||||||
|
export class Upgrade {
|
||||||
|
public settings: pond.UpgradeSettings = pond.UpgradeSettings.create();
|
||||||
|
public status: pond.UpgradeStatus = pond.UpgradeStatus.create();
|
||||||
|
|
||||||
|
public static create(pb?: pond.Upgrade): Upgrade {
|
||||||
|
let my = new Upgrade();
|
||||||
|
if (pb) {
|
||||||
|
my.settings = pond.UpgradeSettings.fromObject(or(pb.settings, {}));
|
||||||
|
my.status = pond.UpgradeStatus.fromObject(or(pb.status, {}));
|
||||||
|
}
|
||||||
|
return my;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static any(data: any): Upgrade {
|
||||||
|
return Upgrade.create(pond.Upgrade.fromObject(cloneDeep(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static clone(other: Upgrade): Upgrade {
|
||||||
|
let my = new Upgrade();
|
||||||
|
my.settings = pond.UpgradeSettings.fromObject(cloneDeep(other.settings));
|
||||||
|
my.status = pond.UpgradeStatus.fromObject(cloneDeep(other.status));
|
||||||
|
return my;
|
||||||
|
}
|
||||||
|
|
||||||
|
public key(): string {
|
||||||
|
return this.settings.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ export * from "./ShareableLink";
|
||||||
export * from "./Site";
|
export * from "./Site";
|
||||||
export * from "./Tag";
|
export * from "./Tag";
|
||||||
export * from "./Task";
|
export * from "./Task";
|
||||||
// export * from "./Upgrade";
|
export * from "./Upgrade";
|
||||||
// export * from "./User";
|
// export * from "./User";
|
||||||
export * from "./user";
|
export * from "./user";
|
||||||
export * from "./Field";
|
export * from "./Field";
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ export default function BottomNavigator(props: Props) {
|
||||||
return (
|
return (
|
||||||
<BottomNavigation value={route} onChange={(_, newValue) => handleRouteChange(newValue)}>
|
<BottomNavigation value={route} onChange={(_, newValue) => handleRouteChange(newValue)}>
|
||||||
{isAdaptive && (
|
{isAdaptive && (
|
||||||
<BottomNavigationAction label="Farm" icon={<FieldsIcon type={getType()} />} value="fields" />
|
<BottomNavigationAction label="Farm" icon={<FieldsIcon type={getType()} />} value="visualFarm" />
|
||||||
)}
|
)}
|
||||||
{isAdaptive && (
|
{isAdaptive && (
|
||||||
<BottomNavigationAction label="Bins" icon={<BinsIcon type={getType()} />} value="bins" />
|
<BottomNavigationAction label="Bins" icon={<BinsIcon type={getType()} />} value="bins" />
|
||||||
|
|
@ -114,7 +114,7 @@ export default function BottomNavigator(props: Props) {
|
||||||
<BottomNavigationAction
|
<BottomNavigationAction
|
||||||
label="Site Map"
|
label="Site Map"
|
||||||
icon={<FieldsIcon type={getType()} />}
|
icon={<FieldsIcon type={getType()} />}
|
||||||
value="constructionsiteMap"
|
value="constructionMap"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isOmni && (
|
{isOmni && (
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { ErrorBoundary } from "react-error-boundary";
|
||||||
import { getWhitelabel } from "services/whiteLabel";
|
import { getWhitelabel } from "services/whiteLabel";
|
||||||
import Ventilation from "pages/VentEditor";
|
import Ventilation from "pages/VentEditor";
|
||||||
import { useGlobalState } from "providers";
|
import { useGlobalState } from "providers";
|
||||||
|
import DeviceSupport from "pages/DeviceSupport";
|
||||||
|
|
||||||
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
||||||
const DevicePage = lazy(() => import("pages/Device"));
|
const DevicePage = lazy(() => import("pages/Device"));
|
||||||
|
|
@ -103,6 +104,10 @@ export default function Router() {
|
||||||
path="/:deviceID" // "/settings/basic"
|
path="/:deviceID" // "/settings/basic"
|
||||||
element={<DevicePage />}
|
element={<DevicePage />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/:deviceID/support" // "/settings/basic"
|
||||||
|
element={<DeviceSupport />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/:deviceID/components/:componentID" // "/settings/basic"
|
path="/:deviceID/components/:componentID" // "/settings/basic"
|
||||||
element={<DeviceComponent />}
|
element={<DeviceComponent />}
|
||||||
|
|
|
||||||
|
|
@ -160,84 +160,6 @@ export default function SideNavigator(props: Props) {
|
||||||
const isAdCon = IsAdCon()
|
const isAdCon = IsAdCon()
|
||||||
return (
|
return (
|
||||||
<List className={classes.list} component="nav">
|
<List className={classes.list} component="nav">
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
|
||||||
<Tooltip title="Bins" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-dashboard"
|
|
||||||
onClick={() => goTo("/bins")}
|
|
||||||
classes={getClasses("/bin")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<BinsIcon height={"26px"}/>
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Bins" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{(isMiPCA || user.hasFeature("admin")) && (
|
|
||||||
<Tooltip title="Terminals" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-terminals"
|
|
||||||
onClick={() => goTo("/terminals")}
|
|
||||||
classes={getClasses("/terminal")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<PlaneIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Terminals" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
<Tooltip title="Devices" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-dashboard"
|
|
||||||
onClick={() => goTo("/devices")}
|
|
||||||
classes={getClasses("/device")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<BindaptIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Devices" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
{(isMiVent || user.hasFeature("admin")) && (
|
|
||||||
<Tooltip title="Mines" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-dashboard"
|
|
||||||
onClick={() => goTo("/mines")}
|
|
||||||
classes={getClasses("/mine")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<MiningIcon height={"26px"} width={"26px"} />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Mines" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
<Tooltip title="Teams" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-dashboard"
|
|
||||||
onClick={() => goTo("/teams")}
|
|
||||||
classes={getClasses("/team")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<People style={{ color: getThemeType() === "light" ? "black" : undefined }}/>
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Teams" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="Users" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-dashboard"
|
|
||||||
onClick={() => goTo("/users")}
|
|
||||||
classes={getClasses("/user")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Person style={{ color: getThemeType() === "light" ? "black" : undefined }}/>
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Users" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
{(isAg || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Visual Farm" placement="right">
|
<Tooltip title="Visual Farm" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
|
@ -252,20 +174,6 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
|
||||||
<Tooltip title="My Fields" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-field-list"
|
|
||||||
onClick={() => goTo("/fields")}
|
|
||||||
classes={getClasses("/fields")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<FieldListIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="My Fields" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{(isMiPCA || user.hasFeature("admin")) && (
|
{(isMiPCA || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Aviation Map" placement="right">
|
<Tooltip title="Aviation Map" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
|
@ -294,6 +202,169 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
{(isAg || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="Contracts" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-contracts"
|
||||||
|
onClick={() => goTo("/contracts")}
|
||||||
|
classes={getClasses("/contracts")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<ContractsIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Contracts" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{(isAg || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="Bins" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-dashboard"
|
||||||
|
onClick={() => goTo("/bins")}
|
||||||
|
classes={getClasses("/bin")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<BinsIcon height={"26px"}/>
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Bins" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{(isMiPCA || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="Terminals" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-terminals"
|
||||||
|
onClick={() => goTo("/terminals")}
|
||||||
|
classes={getClasses("/terminal")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<PlaneIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Terminals" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{(user.hasFeature("installer") && isAg || user.hasFeature("admin")) &&
|
||||||
|
<Tooltip title="Cable Estimator" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-cable-estimator"
|
||||||
|
onClick={() => goTo("/cableEstimate")}
|
||||||
|
classes={getClasses("/cableEstimate")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<CableIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Cable Estimator" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
{(isAg || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="Transactions" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-transactions"
|
||||||
|
onClick={() => goTo("/transactions")}
|
||||||
|
classes={getClasses("/transactions")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<SyncAlt />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Transactions" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{(isMiVent || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="Mines" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-dashboard"
|
||||||
|
onClick={() => goTo("/mines")}
|
||||||
|
classes={getClasses("/mine")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<MiningIcon height={"26px"} width={"26px"} />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Mines" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<Tooltip title="Devices" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-dashboard"
|
||||||
|
onClick={() => goTo("/devices")}
|
||||||
|
classes={getClasses("/device")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<BindaptIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Devices" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
{user.hasFeature("admin") &&
|
||||||
|
<Tooltip title="Firmware" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-firmware"
|
||||||
|
onClick={() => goTo("/firmware")}
|
||||||
|
classes={getClasses("/firmware")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Memory />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Logs" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
{user.hasFeature("admin") &&
|
||||||
|
<Tooltip title="Logs" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-logs"
|
||||||
|
onClick={() => goTo("/logs")}
|
||||||
|
classes={getClasses("/logs")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<DataDuckIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Logs" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
<Tooltip title="Teams" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-dashboard"
|
||||||
|
onClick={() => goTo("/teams")}
|
||||||
|
classes={getClasses("/team")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<People style={{ color: getThemeType() === "light" ? "black" : undefined }}/>
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Teams" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Users" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-dashboard"
|
||||||
|
onClick={() => goTo("/users")}
|
||||||
|
classes={getClasses("/user")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Person style={{ color: getThemeType() === "light" ? "black" : undefined }}/>
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="Users" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
{(isAg || user.hasFeature("admin")) && (
|
||||||
|
<Tooltip title="My Fields" placement="right">
|
||||||
|
<ListItemButton
|
||||||
|
id="tour-field-list"
|
||||||
|
onClick={() => goTo("/fields")}
|
||||||
|
classes={getClasses("/fields")}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
<FieldListIcon />
|
||||||
|
</ListItemIcon>
|
||||||
|
{open && <ListItemText primary="My Fields" />}
|
||||||
|
</ListItemButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
{(isAdCon || user.hasFeature("admin")) && (
|
{(isAdCon || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Jobsites" placement="right">
|
<Tooltip title="Jobsites" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
|
@ -334,48 +405,7 @@ export default function SideNavigator(props: Props) {
|
||||||
{open && <ListItemText primary="Tasks" />}
|
{open && <ListItemText primary="Tasks" />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
|
||||||
<Tooltip title="Transactions" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-transactions"
|
|
||||||
onClick={() => goTo("/transactions")}
|
|
||||||
classes={getClasses("/transactions")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<SyncAlt />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Transactions" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
|
||||||
<Tooltip title="Contracts" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-contracts"
|
|
||||||
onClick={() => goTo("/contracts")}
|
|
||||||
classes={getClasses("/contracts")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<ContractsIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Contracts" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
{(user.hasFeature("installer") && isAg || user.hasFeature("admin")) &&
|
|
||||||
<Tooltip title="Cable Estimator" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-cable-estimator"
|
|
||||||
onClick={() => goTo("/cableEstimate")}
|
|
||||||
classes={getClasses("/cableEstimate")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<CableIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Cable Estimator" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
{user.hasFeature("developer") &&
|
{user.hasFeature("developer") &&
|
||||||
<Tooltip title="API documentation" placement="right">
|
<Tooltip title="API documentation" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
|
@ -390,34 +420,6 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{user.hasFeature("admin") &&
|
|
||||||
<Tooltip title="Firmware" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-firmware"
|
|
||||||
onClick={() => goTo("/firmware")}
|
|
||||||
classes={getClasses("/firmware")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<Memory />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Logs" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
{user.hasFeature("admin") &&
|
|
||||||
<Tooltip title="Logs" placement="right">
|
|
||||||
<ListItemButton
|
|
||||||
id="tour-logs"
|
|
||||||
onClick={() => goTo("/logs")}
|
|
||||||
classes={getClasses("/logs")}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
<DataDuckIcon />
|
|
||||||
</ListItemIcon>
|
|
||||||
{open && <ListItemText primary="Logs" />}
|
|
||||||
</ListItemButton>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
{user.hasFeature("admin") && window.NDEFReader &&
|
{user.hasFeature("admin") && window.NDEFReader &&
|
||||||
<Tooltip title="NFC" placement="right">
|
<Tooltip title="NFC" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ import { makeStyles, styled } from "@mui/styles";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { Controller } from "models/Controller";
|
import { Controller } from "models/Controller";
|
||||||
import TaskViewer from "tasks/TaskViewer";
|
import TaskViewer from "tasks/TaskViewer";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
|
@ -214,54 +215,54 @@ export default function Bin(props: Props) {
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledToggle = styled(ToggleButton)(() => ({
|
// const StyledToggle = styled(ToggleButton)(() => ({
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: "transparent",
|
// backgroundColor: "transparent",
|
||||||
overflow: "visible",
|
// overflow: "visible",
|
||||||
content: "content-box",
|
// content: "content-box",
|
||||||
"&$selected": {
|
// "&$selected": {
|
||||||
backgroundColor: "gold",
|
// backgroundColor: "gold",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
fontWeight: "bold"
|
// fontWeight: "bold"
|
||||||
},
|
// },
|
||||||
"&$selected:hover": {
|
// "&$selected:hover": {
|
||||||
backgroundColor: "rgb(255, 255, 0)",
|
// backgroundColor: "rgb(255, 255, 0)",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24
|
// borderRadius: 24
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
selected: {}
|
// selected: {}
|
||||||
}))
|
// }))
|
||||||
|
|
||||||
const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
// const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
||||||
grouped: {
|
// grouped: {
|
||||||
margin: theme.spacing(-0.5),
|
// margin: theme.spacing(-0.5),
|
||||||
border: "none",
|
// border: "none",
|
||||||
padding: theme.spacing(1),
|
// padding: theme.spacing(1),
|
||||||
"&:not(:first-child):not(:last-child)": {
|
// "&:not(:first-child):not(:last-child)": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.5),
|
// marginRight: theme.spacing(0.5),
|
||||||
marginLeft: theme.spacing(0.5)
|
// marginLeft: theme.spacing(0.5)
|
||||||
},
|
// },
|
||||||
"&:first-child": {
|
// "&:first-child": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginLeft: theme.spacing(0.25)
|
// marginLeft: theme.spacing(0.25)
|
||||||
},
|
// },
|
||||||
"&:last-child": {
|
// "&:last-child": {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.25)
|
// marginRight: theme.spacing(0.25)
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: darken(
|
// backgroundColor: darken(
|
||||||
theme.palette.background.paper,
|
// theme.palette.background.paper,
|
||||||
getThemeType() === "light" ? 0.05 : 0.25
|
// getThemeType() === "light" ? 0.05 : 0.25
|
||||||
),
|
// ),
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
content: "border-box"
|
// content: "border-box"
|
||||||
}
|
// }
|
||||||
}))
|
// }))
|
||||||
|
|
||||||
const load = useCallback(() => {
|
const load = useCallback(() => {
|
||||||
if (loadRef.current || user.id() === "") return;
|
if (loadRef.current || user.id() === "") return;
|
||||||
|
|
@ -547,7 +548,6 @@ export default function Bin(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tasks = () => {
|
const tasks = () => {
|
||||||
console.log(showTasks())
|
|
||||||
if (showTasks()) {
|
if (showTasks()) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|
@ -816,9 +816,9 @@ export default function Bin(props: Props) {
|
||||||
|
|
||||||
const desktopView = () => {
|
const desktopView = () => {
|
||||||
return (
|
return (
|
||||||
<Card style={{ padding: 15 }}>
|
<Card sx={{ padding: 1 }}>
|
||||||
<Grid container spacing={2} direction={"row"} justifyContent="center" alignItems="stretch">
|
<Grid container spacing={2} direction={"row"} justifyContent="center" alignItems="stretch">
|
||||||
<Grid size={{ xs: 12, sm: 12, md: 6, lg: 3 }}>
|
<Grid size={{ xs: 12, sm: 12, md: 12, lg: 4 }}>
|
||||||
<Box
|
<Box
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
marginBottom={1}
|
marginBottom={1}
|
||||||
|
|
@ -844,7 +844,7 @@ export default function Bin(props: Props) {
|
||||||
{overview()}
|
{overview()}
|
||||||
{preferences && binComponents(preferences)}
|
{preferences && binComponents(preferences)}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={{ xs: 12, sm: 12, md: 6, lg: 3 }}>
|
<Grid size={{ xs: 12, sm: 12, md: 12, lg: 2.6 }}>
|
||||||
{(bin.settings.mode === pond.BinMode.BIN_MODE_DRYING ||
|
{(bin.settings.mode === pond.BinMode.BIN_MODE_DRYING ||
|
||||||
bin.settings.mode === pond.BinMode.BIN_MODE_HYDRATING) && (
|
bin.settings.mode === pond.BinMode.BIN_MODE_HYDRATING) && (
|
||||||
<BinConditioningCard
|
<BinConditioningCard
|
||||||
|
|
@ -862,9 +862,34 @@ export default function Bin(props: Props) {
|
||||||
<BinStorageConditions bin={bin} headspaceCO2={headspaceCO2} cables={grainCables} />
|
<BinStorageConditions bin={bin} headspaceCO2={headspaceCO2} cables={grainCables} />
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid id="tour-graphs" size={{ xs: 12, sm: 12, md: 12, lg: 6 }}>
|
<Grid id="tour-graphs" size={{ xs: 12, sm: 12, md: 12, lg: 5.3 }}>
|
||||||
<Box paddingBottom={2}>
|
<Box paddingBottom={2} display="flex">
|
||||||
<StyledToggleButtonGroup
|
<ButtonGroup
|
||||||
|
toggle
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Inventory",
|
||||||
|
function: () => setDetail("inventory")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Sensors",
|
||||||
|
function: () => setDetail("sensors")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Analysis",
|
||||||
|
function: () => setDetail("analytics")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Alerts",
|
||||||
|
function: () => setDetail("alerts")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Presets",
|
||||||
|
function: () => setDetail("presets")
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/* <StyledToggleButtonGroup
|
||||||
id="tour-graph-tabs"
|
id="tour-graph-tabs"
|
||||||
value={detail}
|
value={detail}
|
||||||
exclusive
|
exclusive
|
||||||
|
|
@ -900,7 +925,7 @@ export default function Bin(props: Props) {
|
||||||
aria-label="Bin Mode Presets">
|
aria-label="Bin Mode Presets">
|
||||||
Presets
|
Presets
|
||||||
</StyledToggle>
|
</StyledToggle>
|
||||||
</StyledToggleButtonGroup>
|
</StyledToggleButtonGroup> */}
|
||||||
</Box>
|
</Box>
|
||||||
{detail === "alerts" && (
|
{detail === "alerts" && (
|
||||||
<Box>
|
<Box>
|
||||||
|
|
@ -1089,11 +1114,12 @@ export default function Bin(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer padding={1}>
|
||||||
{showPlenumError()}
|
{showPlenumError()}
|
||||||
<Box
|
<Box
|
||||||
paddingBottom={isMobile || displayMobile ? 1 : 2}
|
paddingBottom={isMobile || displayMobile ? 1 : 2}
|
||||||
paddingX={isMobile || displayMobile ? 1 : 2}>
|
paddingX={1}
|
||||||
|
>
|
||||||
{tabs()}
|
{tabs()}
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<Box
|
<Box
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ import { getThemeType } from "theme/themeType";
|
||||||
import GrainBagSettings from "grainBag/grainBagSettings";
|
import GrainBagSettings from "grainBag/grainBagSettings";
|
||||||
import GrainBagList from "grainBag/grainBagList";
|
import GrainBagList from "grainBag/grainBagList";
|
||||||
import BinYards from "bin/BinYards";
|
import BinYards from "bin/BinYards";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
// import { useHistory } from "react-router";
|
// import { useHistory } from "react-router";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
|
|
@ -200,56 +201,56 @@ export default function Bins(props: Props) {
|
||||||
// fertilizer: 0
|
// fertilizer: 0
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
// const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }: { theme: Theme }) => ({
|
||||||
'& .MuiToggleButtonGroup-grouped': {
|
// '& .MuiToggleButtonGroup-grouped': {
|
||||||
// border: 'none' replaces the old 'grouped' border: "none"
|
// // border: 'none' replaces the old 'grouped' border: "none"
|
||||||
border: 'none',
|
// border: 'none',
|
||||||
padding: theme.spacing(1),
|
// padding: theme.spacing(1),
|
||||||
// Middle buttons
|
// // Middle buttons
|
||||||
'&:not(:first-child):not(:last-child)': {
|
// '&:not(:first-child):not(:last-child)': {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.5),
|
// marginRight: theme.spacing(0.5),
|
||||||
marginLeft: theme.spacing(0.5),
|
// marginLeft: theme.spacing(0.5),
|
||||||
},
|
// },
|
||||||
// First button
|
// // First button
|
||||||
'&:first-child': {
|
// '&:first-child': {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginLeft: theme.spacing(0.25),
|
// marginLeft: theme.spacing(0.25),
|
||||||
},
|
// },
|
||||||
// Last button
|
// // Last button
|
||||||
'&:last-child': {
|
// '&:last-child': {
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
marginRight: theme.spacing(0.25),
|
// marginRight: theme.spacing(0.25),
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
// Root styles
|
// // Root styles
|
||||||
backgroundColor: darken(
|
// backgroundColor: darken(
|
||||||
theme.palette.background.paper,
|
// theme.palette.background.paper,
|
||||||
getThemeType() === 'light' ? 0.05 : 0.25
|
// getThemeType() === 'light' ? 0.05 : 0.25
|
||||||
),
|
// ),
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
boxSizing: 'content-box', // Fixed 'content: "border-box"' typo from original
|
// boxSizing: 'content-box', // Fixed 'content: "border-box"' typo from original
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
const StyledToggle = styled(ToggleButton)(({ theme }: { theme: Theme }) => ({
|
// const StyledToggle = styled(ToggleButton)(({ theme }: { theme: Theme }) => ({
|
||||||
root: {
|
// root: {
|
||||||
backgroundColor: "transparent",
|
// backgroundColor: "transparent",
|
||||||
overflow: "visible",
|
// overflow: "visible",
|
||||||
content: "content-box",
|
// content: "content-box",
|
||||||
"&$selected": {
|
// "&$selected": {
|
||||||
backgroundColor: "gold",
|
// backgroundColor: "gold",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24,
|
// borderRadius: 24,
|
||||||
fontWeight: "bold"
|
// fontWeight: "bold"
|
||||||
},
|
// },
|
||||||
"&$selected:hover": {
|
// "&$selected:hover": {
|
||||||
backgroundColor: "rgb(255, 255, 0)",
|
// backgroundColor: "rgb(255, 255, 0)",
|
||||||
color: "black",
|
// color: "black",
|
||||||
borderRadius: 24
|
// borderRadius: 24
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
selected: {}
|
// selected: {}
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let ebt = sessionStorage.getItem("expandBinTotal");
|
let ebt = sessionStorage.getItem("expandBinTotal");
|
||||||
|
|
@ -502,6 +503,17 @@ export default function Bins(props: Props) {
|
||||||
}
|
}
|
||||||
}, [loadBins, props.insert, contentFilter]);
|
}, [loadBins, props.insert, contentFilter]);
|
||||||
|
|
||||||
|
const determineNodeToggle = () => {
|
||||||
|
switch(cardValDisplay) {
|
||||||
|
case "low":
|
||||||
|
return [0]
|
||||||
|
case "high":
|
||||||
|
return [2]
|
||||||
|
default:
|
||||||
|
return [1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const duplicateBin = (bin: Bin) => {
|
const duplicateBin = (bin: Bin) => {
|
||||||
binAPI.addBin(bin.settings, as).then(resp => {
|
binAPI.addBin(bin.settings, as).then(resp => {
|
||||||
loadBins();
|
loadBins();
|
||||||
|
|
@ -1154,7 +1166,25 @@ export default function Bins(props: Props) {
|
||||||
Load All
|
Load All
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<StyledToggleButtonGroup
|
<ButtonGroup
|
||||||
|
toggle
|
||||||
|
toggledButtons={determineNodeToggle()}
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Low",
|
||||||
|
function: () => {setCardValDisplay("low")}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Average",
|
||||||
|
function: () => {setCardValDisplay("average")}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "High",
|
||||||
|
function: () => {setCardValDisplay("high")}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/* <StyledToggleButtonGroup
|
||||||
id="cardValueDisplay"
|
id="cardValueDisplay"
|
||||||
value={cardValDisplay}
|
value={cardValDisplay}
|
||||||
exclusive
|
exclusive
|
||||||
|
|
@ -1184,12 +1214,33 @@ export default function Bins(props: Props) {
|
||||||
}}>
|
}}>
|
||||||
High
|
High
|
||||||
</StyledToggle>
|
</StyledToggle>
|
||||||
</StyledToggleButtonGroup>
|
</StyledToggleButtonGroup> */}
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid >
|
<Grid >
|
||||||
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
||||||
<StyledToggleButtonGroup
|
<ButtonGroup
|
||||||
|
toggle
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Grid",
|
||||||
|
icon: <ViewComfy />,
|
||||||
|
function: () => {
|
||||||
|
setBinView("grid");
|
||||||
|
sessionStorage.setItem("binsView", "grid");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "List",
|
||||||
|
icon: <ViewList />,
|
||||||
|
function: () => {
|
||||||
|
setBinView("list");
|
||||||
|
sessionStorage.setItem("binsView", "list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/* <StyledToggleButtonGroup
|
||||||
id="tour-graph-tabs"
|
id="tour-graph-tabs"
|
||||||
value={binView}
|
value={binView}
|
||||||
exclusive
|
exclusive
|
||||||
|
|
@ -1203,7 +1254,7 @@ export default function Bins(props: Props) {
|
||||||
sessionStorage.setItem("binsView", "grid");
|
sessionStorage.setItem("binsView", "grid");
|
||||||
}}>
|
}}>
|
||||||
<ViewComfy />
|
<ViewComfy />
|
||||||
</StyledToggle>
|
</StyledToggle> */}
|
||||||
{/* hidden at dustins request so that grid view and list are the only two */}
|
{/* hidden at dustins request so that grid view and list are the only two */}
|
||||||
{/* <StyledToggle
|
{/* <StyledToggle
|
||||||
value={"scroll"}
|
value={"scroll"}
|
||||||
|
|
@ -1214,7 +1265,7 @@ export default function Bins(props: Props) {
|
||||||
}}>
|
}}>
|
||||||
<ViewColumn />
|
<ViewColumn />
|
||||||
</StyledToggle> */}
|
</StyledToggle> */}
|
||||||
<StyledToggle
|
{/* <StyledToggle
|
||||||
value={"list"}
|
value={"list"}
|
||||||
aria-label="list view"
|
aria-label="list view"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -1223,7 +1274,7 @@ export default function Bins(props: Props) {
|
||||||
}}>
|
}}>
|
||||||
<ViewList />
|
<ViewList />
|
||||||
</StyledToggle>
|
</StyledToggle>
|
||||||
</StyledToggleButtonGroup>
|
</StyledToggleButtonGroup> */}
|
||||||
<MoreVert
|
<MoreVert
|
||||||
className={classes.icon}
|
className={classes.icon}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
|
|
@ -1261,7 +1312,28 @@ export default function Bins(props: Props) {
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid >
|
<Grid >
|
||||||
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
<Box display="flex" paddingY={1} alignContent="center" alignItems="center">
|
||||||
<StyledToggleButtonGroup
|
<ButtonGroup
|
||||||
|
toggle
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Grid",
|
||||||
|
icon: <ViewComfy />,
|
||||||
|
function: () => {
|
||||||
|
setBagView("grid");
|
||||||
|
sessionStorage.setItem("bagsView", "grid");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "List",
|
||||||
|
icon: <ViewList />,
|
||||||
|
function: () => {
|
||||||
|
setBagView("list");
|
||||||
|
sessionStorage.setItem("bagsView", "list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/* <StyledToggleButtonGroup
|
||||||
id="tour-graph-tabs"
|
id="tour-graph-tabs"
|
||||||
value={bagView}
|
value={bagView}
|
||||||
exclusive
|
exclusive
|
||||||
|
|
@ -1275,7 +1347,7 @@ export default function Bins(props: Props) {
|
||||||
sessionStorage.setItem("bagsView", "grid");
|
sessionStorage.setItem("bagsView", "grid");
|
||||||
}}>
|
}}>
|
||||||
<ViewComfy />
|
<ViewComfy />
|
||||||
</StyledToggle>
|
</StyledToggle> */}
|
||||||
{/* hidden at dustins request so that grid view and list are the only two */}
|
{/* hidden at dustins request so that grid view and list are the only two */}
|
||||||
{/* <StyledToggle
|
{/* <StyledToggle
|
||||||
value={"scroll"}
|
value={"scroll"}
|
||||||
|
|
@ -1286,7 +1358,7 @@ export default function Bins(props: Props) {
|
||||||
}}>
|
}}>
|
||||||
<ViewColumn />
|
<ViewColumn />
|
||||||
</StyledToggle> */}
|
</StyledToggle> */}
|
||||||
<StyledToggle
|
{/* <StyledToggle
|
||||||
value={"list"}
|
value={"list"}
|
||||||
aria-label="list view"
|
aria-label="list view"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -1295,7 +1367,7 @@ export default function Bins(props: Props) {
|
||||||
}}>
|
}}>
|
||||||
<ViewList />
|
<ViewList />
|
||||||
</StyledToggle>
|
</StyledToggle>
|
||||||
</StyledToggleButtonGroup>
|
</StyledToggleButtonGroup> */}
|
||||||
<IconButton
|
<IconButton
|
||||||
className={classes.icon}
|
className={classes.icon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,15 @@ import { or } from "utils";
|
||||||
import ComponentDiagnostics from "component/ComponentDiagnostics";
|
import ComponentDiagnostics from "component/ComponentDiagnostics";
|
||||||
import DeviceWizard from "device/DeviceWizard";
|
import DeviceWizard from "device/DeviceWizard";
|
||||||
|
|
||||||
|
export interface DevicePageData {
|
||||||
|
device: Device;
|
||||||
|
components: Component[];
|
||||||
|
interactions: Interaction[];
|
||||||
|
permissions: pond.Permission[];
|
||||||
|
preferences: pond.DevicePreferences;
|
||||||
|
// componentPreferences: Map<string, pond.DeviceComponentPreferences>
|
||||||
|
}
|
||||||
|
|
||||||
export default function DevicePage() {
|
export default function DevicePage() {
|
||||||
const deviceAPI = useDeviceAPI()
|
const deviceAPI = useDeviceAPI()
|
||||||
const snackbar = useSnackbar()
|
const snackbar = useSnackbar()
|
||||||
|
|
@ -30,6 +39,7 @@ export default function DevicePage() {
|
||||||
const groupID = useParams<{ groupID: string }>()?.groupID ?? "";
|
const groupID = useParams<{ groupID: string }>()?.groupID ?? "";
|
||||||
const { state } = useLocation();
|
const { state } = useLocation();
|
||||||
const [{ as, team, user }] = useGlobalState()
|
const [{ as, team, user }] = useGlobalState()
|
||||||
|
// console.log(state)
|
||||||
const [device, setDevice] = useState<Device>(state?.device ? Device.create(state.device) : Device.create())
|
const [device, setDevice] = useState<Device>(state?.device ? Device.create(state.device) : Device.create())
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [permissions, setPermissions] = useState<pond.Permission[]>([])
|
const [permissions, setPermissions] = useState<pond.Permission[]>([])
|
||||||
|
|
@ -51,11 +61,21 @@ export default function DevicePage() {
|
||||||
|
|
||||||
const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false)
|
const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false)
|
||||||
|
|
||||||
|
// const [devicePageData, setDevicePageData] = useState(pond.GetDevicePageDataResponse.create())
|
||||||
|
|
||||||
const loadDevice = () => {
|
const loadDevice = () => {
|
||||||
// console.log("load device page data")
|
|
||||||
if (loading) return
|
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)
|
setLoading(true)
|
||||||
deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes(), as).then(resp => {
|
deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes(), as).then(resp => {
|
||||||
|
// setDevicePageData(resp.data)
|
||||||
let device = Device.any(resp.data.device)
|
let device = Device.any(resp.data.device)
|
||||||
// console.log(resp.data.device)
|
// console.log(resp.data.device)
|
||||||
setDevice(device)
|
setDevice(device)
|
||||||
|
|
@ -418,19 +438,9 @@ export default function DevicePage() {
|
||||||
<PageContainer padding={isMobile ? 0 : 2}>
|
<PageContainer padding={isMobile ? 0 : 2}>
|
||||||
<Grid container justifyContent={"space-between"}>
|
<Grid container justifyContent={"space-between"}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<SmartBreadcrumb />
|
<SmartBreadcrumb deviceName={device.name()} />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<AddComponentManualDialog
|
|
||||||
device={device.id()}
|
|
||||||
open={addComponentManualDialogOpen}
|
|
||||||
onClose={() => setAddComponentManualDialogOpen(false)}
|
|
||||||
/>
|
|
||||||
{user.hasFeature("developer") === true &&
|
|
||||||
<Button onClick={() => setAddComponentManualDialogOpen(true)}>
|
|
||||||
Manual Comp
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
<DeviceActions
|
<DeviceActions
|
||||||
device={device}
|
device={device}
|
||||||
isPaused={false}
|
isPaused={false}
|
||||||
|
|
@ -449,8 +459,6 @@ export default function DevicePage() {
|
||||||
<DeviceOverview
|
<DeviceOverview
|
||||||
device={device}
|
device={device}
|
||||||
components={[...components.values()]}
|
components={[...components.values()]}
|
||||||
// components={[]}
|
|
||||||
// components={components}
|
|
||||||
usage={getUsage()}
|
usage={getUsage()}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
groupID={parseInt(groupID)}
|
groupID={parseInt(groupID)}
|
||||||
|
|
|
||||||
393
src/pages/DeviceSupport.tsx
Normal file
|
|
@ -0,0 +1,393 @@
|
||||||
|
import { Box, Button, FormControl, Grid2 as Grid, MenuItem, Select, Tab, Tabs, TextField, Typography, useTheme } from "@mui/material";
|
||||||
|
import PageContainer from "./PageContainer";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import LogsDisplay from "common/LogsDisplay";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useDeviceAPI, useSnackbar } from "hooks";
|
||||||
|
import { useLocation, useParams } from "react-router-dom";
|
||||||
|
import { Component, Device, Interaction } from "models";
|
||||||
|
import { getContextKeys, getContextTypes } from "pbHelpers/Context";
|
||||||
|
import SmartBreadcrumb from "common/SmartBreadcrumb";
|
||||||
|
import { DevicePageData } from "./Device";
|
||||||
|
import AddComponentManualDialog from "component/AddComponentManualDialog";
|
||||||
|
import { useGlobalState } from "providers";
|
||||||
|
import DeviceActions from "device/DeviceActions";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { FindAvailablePositions } from "pbHelpers/DeviceAvailability";
|
||||||
|
import LoadingScreen from "app/LoadingScreen";
|
||||||
|
import DeviceOverview from "device/DeviceOverview";
|
||||||
|
|
||||||
|
interface TabPanelProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
index: any;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabPanelMine(props: TabPanelProps) {
|
||||||
|
const { children, value, index, ...other } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="tabpanel"
|
||||||
|
hidden={value !== index}
|
||||||
|
aria-labelledby={`simple-tab-${index}`}
|
||||||
|
{...other}>
|
||||||
|
{value === index && <>{children}</>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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>(Device.create())
|
||||||
|
const [devicePageData, setDevicePageData] = useState<DevicePageData>(state?.devicePageData ? state.devicePageData : undefined)
|
||||||
|
const [tabNumber, setTabNumber] = useState(0)
|
||||||
|
const [newCap, setNewCap] = useState(0);
|
||||||
|
const [datacap, setDatacap] = useState<number>();
|
||||||
|
const [isOver, setIsOver] = useState(undefined);
|
||||||
|
const [pauseCheck, setPauseCheck] = useState(undefined);
|
||||||
|
const [inputFirmware, setInputFirmware] = useState<string>("");
|
||||||
|
const [platform, setPlatform] = useState<pond.DevicePlatform>(
|
||||||
|
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 (
|
||||||
|
<LoadingScreen message="Getting device data"/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Grid container justifyContent={"space-between"}>
|
||||||
|
<Grid>
|
||||||
|
<SmartBreadcrumb deviceName={device.name()} state={{devicePageData: devicePageData}} />
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<DeviceActions
|
||||||
|
device={device}
|
||||||
|
isPaused={false}
|
||||||
|
isLoading={loading}
|
||||||
|
permissions={devicePageData.permissions}
|
||||||
|
preferences={devicePageData.preferences}
|
||||||
|
toggleNotificationPreference={toggleNotificationPreference}
|
||||||
|
refreshCallback={() => {}}
|
||||||
|
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))}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
{/* <SmartBreadcrumb deviceName={device.name()} state={{devicePageData: devicePageData}} /> */}
|
||||||
|
<DeviceOverview device={device} components={devicePageData.components}/>
|
||||||
|
<br/>
|
||||||
|
<Tabs
|
||||||
|
style={{ position: "sticky" }}
|
||||||
|
value={tabNumber}
|
||||||
|
onChange={handleChange}
|
||||||
|
centered
|
||||||
|
TabIndicatorProps={{ style: { background: "rgba(255,255,0,255)" } }}>
|
||||||
|
<Tab label="Control Center" />
|
||||||
|
<Tab label="Hologram" />
|
||||||
|
<Tab label="DataDog" />
|
||||||
|
<Tab label="DuckData" />
|
||||||
|
</Tabs>
|
||||||
|
<TabPanelMine value={tabNumber} index={0}>
|
||||||
|
<Box>
|
||||||
|
<Typography gutterBottom variant="h5">
|
||||||
|
General Options:
|
||||||
|
</Typography>
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid>
|
||||||
|
<Button color="primary" onClick={resetQuackCount}>
|
||||||
|
Reset Count Rx (safe, use when quack count errors)
|
||||||
|
</Button>
|
||||||
|
<Button color="primary" onClick={resetQuackCountTx}>
|
||||||
|
Reset Count Tx, Are you sure (ONLY use when device not responding)
|
||||||
|
</Button>
|
||||||
|
<Button color="primary" onClick={resetQuackCountTx1000}>
|
||||||
|
Reset Count Tx - 1000, Are you sure (ONLY use when device not responding)
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Button color="primary" onClick={clearPending}>
|
||||||
|
Clear Pending
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<AddComponentManualDialog
|
||||||
|
device={device.id()}
|
||||||
|
open={addComponentManualDialogOpen}
|
||||||
|
onClose={() => setAddComponentManualDialogOpen(false)}
|
||||||
|
/>
|
||||||
|
{user.hasFeature("developer") === true &&
|
||||||
|
<Button onClick={() => setAddComponentManualDialogOpen(true)}>
|
||||||
|
Manual Comp
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</Grid>
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid>
|
||||||
|
<TextField
|
||||||
|
value={inputFirmware}
|
||||||
|
onChange={e => setInputFirmware(e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
<Button color="primary" onClick={() => setFirmwareVersion(inputFirmware)}>
|
||||||
|
Set Firmware
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid>
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
labelId="demo-simple-select-label"
|
||||||
|
id="demo-simple-select"
|
||||||
|
value={platform}
|
||||||
|
label="Platform"
|
||||||
|
onChange={(event: any) => setPlatform(event.target.value)}>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_PHOTON}>Photon</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_ELECTRON}>
|
||||||
|
Electron
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR}>
|
||||||
|
V2 Cellular
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_S3}>
|
||||||
|
V2 Wifi S3
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLACK}>
|
||||||
|
V2 Cellular Black
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_GREEN}>
|
||||||
|
V2 Cellular Green
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_WIFI_BLUE}>
|
||||||
|
V2 Wifi Blue
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.DevicePlatform.DEVICE_PLATFORM_V2_CELLULAR_BLUE}>
|
||||||
|
V2 Cellular Blue
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<Button color="primary" onClick={() => updateDevicePlatform(platform)}>
|
||||||
|
Set Platform
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
<Box marginTop={theme.spacing(0.5)}>
|
||||||
|
<Typography gutterBottom variant="h5">
|
||||||
|
OPI Cable Functions:
|
||||||
|
</Typography>
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid>
|
||||||
|
<Button color="primary">Cable Reprogrammer</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Button color="primary">Cable Diagnostic</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</TabPanelMine>
|
||||||
|
<TabPanelMine value={tabNumber} index={1}>
|
||||||
|
<Grid container direction="column">
|
||||||
|
<Grid container direction="row">
|
||||||
|
<Grid>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
value={newCap}
|
||||||
|
onChange={e => setNewCap(+e.target.value)}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Button onClick={updateDatacap} color="primary">
|
||||||
|
Set New Datacap
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container direction="row" alignItems="center">
|
||||||
|
<Grid>
|
||||||
|
<Button onClick={getDatacap} color="primary">
|
||||||
|
DataCap
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Box margin={3}>{datacap && datacap / 1000 / 1000}Mb</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container direction="row" alignItems="center">
|
||||||
|
<Grid>
|
||||||
|
<Button onClick={checkLimit} color="primary">
|
||||||
|
OverLimit
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Box margin={3}>
|
||||||
|
{isOver === undefined ? "Click to Check" : isOver ? "Over Limit" : "Under Limit"}
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid container direction="row" alignItems="center">
|
||||||
|
<Grid>
|
||||||
|
<Button onClick={checkIfPaused} color="primary">
|
||||||
|
Am I Paused
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Box margin={3}>
|
||||||
|
{pauseCheck === undefined ? "Click to Check" : pauseCheck ? "Paused" : "Running"}
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Button onClick={pauseDevice} color="primary">
|
||||||
|
Pause Me
|
||||||
|
</Button>
|
||||||
|
<Button onClick={resumeDevice} color="primary">
|
||||||
|
Get Me Goin
|
||||||
|
</Button>
|
||||||
|
</TabPanelMine>
|
||||||
|
<TabPanelMine value={tabNumber} index={2}>
|
||||||
|
<LogsDisplay deviceID={deviceID} />
|
||||||
|
</TabPanelMine>
|
||||||
|
<TabPanelMine value={tabNumber} index={3}>
|
||||||
|
History Not imported yet
|
||||||
|
{/* <CompleteHistory device={device} /> */}
|
||||||
|
</TabPanelMine>
|
||||||
|
</PageContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -57,7 +57,14 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
margin: theme.spacing(1),
|
margin: theme.spacing(1),
|
||||||
marginLeft: theme.spacing(2),
|
marginLeft: theme.spacing(2),
|
||||||
display: "flex",
|
display: "flex",
|
||||||
width: theme.spacing(26)
|
width: theme.spacing(32)
|
||||||
|
// justifyContent: "center",
|
||||||
|
},
|
||||||
|
tagsCellContainer: {
|
||||||
|
margin: theme.spacing(1),
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
display: "flex",
|
||||||
|
width: theme.spacing(24)
|
||||||
// justifyContent: "center",
|
// justifyContent: "center",
|
||||||
},
|
},
|
||||||
green: {
|
green: {
|
||||||
|
|
@ -328,8 +335,8 @@ export default function Devices() {
|
||||||
<Box className={classes.descriptionCellContainer}>
|
<Box className={classes.descriptionCellContainer}>
|
||||||
<Tooltip title={device.settings?.description}>
|
<Tooltip title={device.settings?.description}>
|
||||||
<span>
|
<span>
|
||||||
{description.length > 32
|
{description.length > 36
|
||||||
? description.substring(0, 32) + "..."
|
? description.substring(0, 36) + "..."
|
||||||
: description}
|
: description}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
@ -341,7 +348,7 @@ export default function Devices() {
|
||||||
title: "Tags",
|
title: "Tags",
|
||||||
render: (device: Device) => {
|
render: (device: Device) => {
|
||||||
return (
|
return (
|
||||||
<Grid2 container spacing={1} className={classes.descriptionCellContainer}>
|
<Grid2 container spacing={1} className={classes.tagsCellContainer}>
|
||||||
{device.status.tags.map((tag: pond.TagSettings) => (
|
{device.status.tags.map((tag: pond.TagSettings) => (
|
||||||
<Grid2 key={"device-"+device.id()+"-tag-"+tag.key}>
|
<Grid2 key={"device-"+device.id()+"-tag-"+tag.key}>
|
||||||
<TagUI tag={Tag.any({ settings: tag })} />
|
<TagUI tag={Tag.any({ settings: tag })} />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import PageContainer from "./PageContainer";
|
import PageContainer from "./PageContainer";
|
||||||
import FieldList from "field/FieldList";
|
import FieldList from "field/FieldList";
|
||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ export default function Firmware() {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const load = useCallback(() => {
|
const load = useCallback(() => {
|
||||||
firmwareAPI.listFirmware(pageSize, pageSize * tablePage, "asc", "version", searchText)
|
firmwareAPI.listFirmware(pageSize, pageSize * tablePage, "desc", "version", searchText)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setTotal(response.data.total ? response.data.total : 0)
|
setTotal(response.data.total ? response.data.total : 0)
|
||||||
let rows: FirmwareModel[] = or(response.data.firmware, []).map(
|
let rows: FirmwareModel[] = or(response.data.firmware, []).map(
|
||||||
|
|
|
||||||
|
|
@ -339,6 +339,7 @@ export default function Gate(props: Props) {
|
||||||
}}
|
}}
|
||||||
style={{ height: 150, width: "100%", margin: -15 }}>
|
style={{ height: 150, width: "100%", margin: -15 }}>
|
||||||
<Grid
|
<Grid
|
||||||
|
width="100%"
|
||||||
container
|
container
|
||||||
direction="row"
|
direction="row"
|
||||||
spacing={2}
|
spacing={2}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const HTTPContext = createContext<IHTTPContext>({} as IHTTPContext);
|
||||||
|
|
||||||
export default function HTTPProvider(props: Props) {
|
export default function HTTPProvider(props: Props) {
|
||||||
const { children, token } = props;
|
const { children, token } = props;
|
||||||
const { isAuthenticated, /*loginWithRedirect,*/ loginWithPopup } = useAuth0();
|
const { isAuthenticated, loginWithRedirect } = useAuth0();
|
||||||
|
|
||||||
const defaultOptions = (demo: boolean = false) => {
|
const defaultOptions = (demo: boolean = false) => {
|
||||||
if (demo || !isAuthenticated || !token) {
|
if (demo || !isAuthenticated || !token) {
|
||||||
|
|
@ -79,7 +79,7 @@ export default function HTTPProvider(props: Props) {
|
||||||
|
|
||||||
function get<T>(url: string, spreadOptions?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
|
function get<T>(url: string, spreadOptions?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
|
||||||
if (isTokenExpired(token)) {
|
if (isTokenExpired(token)) {
|
||||||
loginWithPopup()
|
loginWithRedirect()
|
||||||
}
|
}
|
||||||
return axios.get(url, {...defaultOptions(), ...spreadOptions});
|
return axios.get(url, {...defaultOptions(), ...spreadOptions});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { useHTTP, usePermissionAPI } from "hooks";
|
import { useHTTP, usePermissionAPI } from "hooks";
|
||||||
// import { useWebsocket } from "websocket";
|
import { Component, deviceScope, User } from "models";
|
||||||
import { /*Component,*/ Component, deviceScope, Team, User } from "models";
|
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { createContext, PropsWithChildren, useContext } from "react";
|
import { createContext, PropsWithChildren, useContext } from "react";
|
||||||
import { pondURL } from "./pond";
|
import { pondURL } from "./pond";
|
||||||
|
|
@ -9,7 +8,6 @@ import { useGlobalState } from "providers";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { or } from "utils/types";
|
import { or } from "utils/types";
|
||||||
import { dateRange } from "providers/http";
|
import { dateRange } from "providers/http";
|
||||||
// import { reject, result } from "lodash";
|
|
||||||
|
|
||||||
export interface IDeviceAPIContext {
|
export interface IDeviceAPIContext {
|
||||||
add: (name: string, description: string, backpack: pond.BackpackSettings, otherTeam?: string) => Promise<AxiosResponse<pond.AddDeviceResponse>>;
|
add: (name: string, description: string, backpack: pond.BackpackSettings, otherTeam?: string) => Promise<AxiosResponse<pond.AddDeviceResponse>>;
|
||||||
|
|
@ -213,12 +211,27 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
|
||||||
types?: string[]
|
types?: string[]
|
||||||
) => {
|
) => {
|
||||||
let url = pondURL("/deviceStatistics");
|
let url = pondURL("/deviceStatistics");
|
||||||
if (types && types.length > 0)
|
if (types && types.length > 0) {
|
||||||
url = pondURL(
|
url = pondURL(
|
||||||
"/deviceStatistics/" +
|
"/deviceStatistics/" +
|
||||||
(keys ? "?keys=" + keys.toString() : "") +
|
(keys ? "?keys=" + keys.toString() : "") +
|
||||||
(types ? "&types=" + types.toString() : "")
|
(types ? "&types=" + types.toString() : "")
|
||||||
)
|
)
|
||||||
|
if (!types.includes("team") && as.length > 0) {
|
||||||
|
url = pondURL(
|
||||||
|
"/deviceStatistics/" +
|
||||||
|
(keys ? "?keys=" + keys.toString() : "") +
|
||||||
|
(types ? "&types=" + types.toString() : "") +
|
||||||
|
("&as=" + as)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (as.length > 0) {
|
||||||
|
url = pondURL(
|
||||||
|
"/deviceStatistics/?as=" + as
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
return new Promise<AxiosResponse<pond.GetDeviceStatisticsResponse>>((resolve, reject) => {
|
return new Promise<AxiosResponse<pond.GetDeviceStatisticsResponse>>((resolve, reject) => {
|
||||||
get<pond.GetDeviceStatisticsResponse>(url).then(resp => {
|
get<pond.GetDeviceStatisticsResponse>(url).then(resp => {
|
||||||
resp.data = pond.GetDeviceStatisticsResponse.fromObject(resp.data)
|
resp.data = pond.GetDeviceStatisticsResponse.fromObject(resp.data)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ interface WhiteLabel {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const STAGING_WHITELABEL: WhiteLabel = {
|
const STAGING_WHITELABEL: WhiteLabel = {
|
||||||
name: import.meta.env.VITE_APP_WEBSITE_TITLE,
|
name: "Staging",
|
||||||
primaryColour: import.meta.env.VITE_APP_PRIMARY_COLOUR,
|
primaryColour: import.meta.env.VITE_APP_PRIMARY_COLOUR,
|
||||||
secondaryColour: import.meta.env.VITE_APP_SECONDARY_COLOUR,
|
secondaryColour: import.meta.env.VITE_APP_SECONDARY_COLOUR,
|
||||||
signatureColour: import.meta.env.VITE_APP_SIGNATURE_COLOUR,
|
signatureColour: import.meta.env.VITE_APP_SIGNATURE_COLOUR,
|
||||||
|
|
@ -123,7 +123,7 @@ const ADAPTIVE_AGRICULTURE_WHITE_LABEL: WhiteLabel = {
|
||||||
// secondaryColour: "#FFFF00",
|
// secondaryColour: "#FFFF00",
|
||||||
signatureColour: "#272727",
|
signatureColour: "#272727",
|
||||||
signatureAccentColour: "#fff",
|
signatureAccentColour: "#fff",
|
||||||
auth0ClientId: import.meta.env.REACT_APP_AUTH0_ADAPTIVE_AGRICULTURE_CLIENT_ID,
|
auth0ClientId: import.meta.env.VITE_AUTH0_ADAPTIVE_AGRICULTURE_CLIENT_ID,
|
||||||
redirectOnLogout: true,
|
redirectOnLogout: true,
|
||||||
logoutRedirectTarget: "https://adaptiveagriculture.ca",
|
logoutRedirectTarget: "https://adaptiveagriculture.ca",
|
||||||
darkLogo: AdapativeAgLogo,
|
darkLogo: AdapativeAgLogo,
|
||||||
|
|
@ -163,7 +163,7 @@ const AEROGROW_WHITE_LABEL: WhiteLabel = {
|
||||||
secondaryColour: "cyan",
|
secondaryColour: "cyan",
|
||||||
signatureColour: "#fff",
|
signatureColour: "#fff",
|
||||||
signatureAccentColour: "#000",
|
signatureAccentColour: "#000",
|
||||||
auth0ClientId: import.meta.env.REACT_APP_AUTH0_AEROGROW_CLIENT_ID,
|
auth0ClientId: import.meta.env.VITE_AUTH0_AEROGROW_CLIENT_ID,
|
||||||
redirectOnLogout: true,
|
redirectOnLogout: true,
|
||||||
logoutRedirectTarget: "https://www.aerogrowmanufacturing.com",
|
logoutRedirectTarget: "https://www.aerogrowmanufacturing.com",
|
||||||
darkLogo: AeroGrowDarkLogo,
|
darkLogo: AeroGrowDarkLogo,
|
||||||
|
|
@ -189,7 +189,7 @@ const MIVENT_WHITE_LABEL: WhiteLabel = {
|
||||||
secondaryColour: "yellow",
|
secondaryColour: "yellow",
|
||||||
signatureColour: "#272727",
|
signatureColour: "#272727",
|
||||||
signatureAccentColour: "#fff",
|
signatureAccentColour: "#fff",
|
||||||
auth0ClientId: import.meta.env.REACT_APP_AUTH0_MIVENT_CLIENT_ID,
|
auth0ClientId: import.meta.env.VITE_AUTH0_MIVENT_CLIENT_ID,
|
||||||
redirectOnLogout: true,
|
redirectOnLogout: true,
|
||||||
logoutRedirectTarget: "https://mivent.ca",
|
logoutRedirectTarget: "https://mivent.ca",
|
||||||
darkLogo: MiVentLightLogo,
|
darkLogo: MiVentLightLogo,
|
||||||
|
|
@ -207,7 +207,7 @@ const ADAPTIVE_CONSTRUCTION_WHITE_LABEL: WhiteLabel = {
|
||||||
secondaryColour: "blue",
|
secondaryColour: "blue",
|
||||||
signatureColour: "#272727",
|
signatureColour: "#272727",
|
||||||
signatureAccentColour: "#fff",
|
signatureAccentColour: "#fff",
|
||||||
auth0ClientId: import.meta.env.REACT_APP_AUTH0_ADAPTIVE_CONSTRUCTION_CLIENT_ID,
|
auth0ClientId: import.meta.env.VITE_AUTH0_ADAPTIVE_CONSTRUCTION_CLIENT_ID,
|
||||||
redirectOnLogout: true,
|
redirectOnLogout: true,
|
||||||
logoutRedirectTarget: "https://adaptiveconstruction.ca",
|
logoutRedirectTarget: "https://adaptiveconstruction.ca",
|
||||||
darkLogo: AdConLogo,
|
darkLogo: AdConLogo,
|
||||||
|
|
@ -233,7 +233,7 @@ const OMNIAIR_WHITE_LABEL: WhiteLabel = {
|
||||||
secondaryColour: "yellow",
|
secondaryColour: "yellow",
|
||||||
signatureColour: "#272727",
|
signatureColour: "#272727",
|
||||||
signatureAccentColour: "#fff",
|
signatureAccentColour: "#fff",
|
||||||
auth0ClientId: import.meta.env.REACT_APP_AUTH0_OMNIAIR_CLIENT_ID,
|
auth0ClientId: import.meta.env.VITE_AUTH0_OMNIAIR_CLIENT_ID,
|
||||||
redirectOnLogout: true,
|
redirectOnLogout: true,
|
||||||
logoutRedirectTarget: "https://omniairsystems.com",
|
logoutRedirectTarget: "https://omniairsystems.com",
|
||||||
darkLogo: MiPCALogo,
|
darkLogo: MiPCALogo,
|
||||||
|
|
@ -259,7 +259,7 @@ const whitelabels = new Map<string, WhiteLabel>([
|
||||||
["brandxducks", BXT_WHITE_LABEL],
|
["brandxducks", BXT_WHITE_LABEL],
|
||||||
["brandxtech", BXT_WHITE_LABEL],
|
["brandxtech", BXT_WHITE_LABEL],
|
||||||
["aerogrowmanufacturing", AEROGROW_WHITE_LABEL],
|
["aerogrowmanufacturing", AEROGROW_WHITE_LABEL],
|
||||||
["localhost", STAGING_WHITELABEL],
|
["localhost", BXT_WHITE_LABEL],
|
||||||
["staging", STAGING_WHITELABEL],
|
["staging", STAGING_WHITELABEL],
|
||||||
["10.0", ADAPTIVE_CONSTRUCTION_WHITE_LABEL],
|
["10.0", ADAPTIVE_CONSTRUCTION_WHITE_LABEL],
|
||||||
["mivent", MIVENT_WHITE_LABEL],
|
["mivent", MIVENT_WHITE_LABEL],
|
||||||
|
|
@ -276,7 +276,7 @@ export function getWhitelabel(): WhiteLabel {
|
||||||
if (window.location.origin.includes("bxt-dev")) {
|
if (window.location.origin.includes("bxt-dev")) {
|
||||||
return BXT_WHITE_LABEL;
|
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;
|
return STAGING_WHITELABEL;
|
||||||
}
|
}
|
||||||
const whiteLabelKeys = Array.from(whitelabels.keys());
|
const whiteLabelKeys = Array.from(whitelabels.keys());
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Task } from "models";
|
||||||
//import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab";
|
//import { ToggleButton, ToggleButtonGroup } from "@material-ui/lab";
|
||||||
import TasksIcon from "products/Construction/TasksIcon";
|
import TasksIcon from "products/Construction/TasksIcon";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import ButtonGroup from "common/ButtonGroup";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tasks: Task[];
|
tasks: Task[];
|
||||||
|
|
@ -147,8 +148,21 @@ export default function TaskList(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Box padding="5px" textAlign="center">
|
<Box padding="5px" textAlign="center">
|
||||||
<Typography variant="h5">{label ? label : ""}</Typography>
|
<Typography variant="h5">{label ? label : ""}</Typography>
|
||||||
<Box textAlign="center">
|
<Box textAlign="center" display="flex" justifyContent="center">
|
||||||
<ToggleButtonGroup value={viewing} exclusive size="small" aria-label="Bin Mode">
|
<ButtonGroup
|
||||||
|
toggle
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
title: "Upcoming",
|
||||||
|
function: () => setViewing("upcoming")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Complete",
|
||||||
|
function: () => setViewing("complete")
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{/* <ToggleButtonGroup value={viewing} exclusive size="small" aria-label="Bin Mode">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
value={"upcoming"}
|
value={"upcoming"}
|
||||||
aria-label="upcoming"
|
aria-label="upcoming"
|
||||||
|
|
@ -161,7 +175,7 @@ export default function TaskList(props: Props) {
|
||||||
aria-label="complete">
|
aria-label="complete">
|
||||||
Complete
|
Complete
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup> */}
|
||||||
{location !== "/tasks" && (
|
{location !== "/tasks" && (
|
||||||
<IconButton id="tasks" component={Link} to="/tasks">
|
<IconButton id="tasks" component={Link} to="/tasks">
|
||||||
<TasksIcon />
|
<TasksIcon />
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ export default function TeamList() {
|
||||||
viewAs = undefined
|
viewAs = undefined
|
||||||
}
|
}
|
||||||
teamAPI
|
teamAPI
|
||||||
.listTeams(limit, limit * page, undefined, "name", undefined, undefined, viewAs, search)
|
.listTeams(limit, limit * page, undefined, "name", undefined, undefined, " ", search)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
setTotal(resp.data.total ? resp.data.total : 0);
|
setTotal(resp.data.total ? resp.data.total : 0);
|
||||||
let newTeams: Team[] = []
|
let newTeams: Team[] = []
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function TeamSearch(props: Props) {
|
||||||
if (!setSearch) return;
|
if (!setSearch) return;
|
||||||
if (searchText === undefined) return;
|
if (searchText === undefined) return;
|
||||||
setHandler(setTimeout(() => {
|
setHandler(setTimeout(() => {
|
||||||
setSearchText(searchText);
|
setSearch(searchText);
|
||||||
}, 750)); // Delay of 750ms
|
}, 750)); // Delay of 750ms
|
||||||
|
|
||||||
return () => clearTimeout(handler); // Cleanup on change
|
return () => clearTimeout(handler); // Cleanup on change
|
||||||
|
|
@ -41,7 +41,7 @@ export default function TeamSearch(props: Props) {
|
||||||
let options: Option[] = [];
|
let options: Option[] = [];
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
teamAPI
|
teamAPI
|
||||||
.listTeams(10, 0, "desc", undefined, undefined, undefined, undefined, search)
|
.listTeams(10, 0, "desc", undefined, undefined, undefined, " ", search)
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (loadUsers)
|
if (loadUsers)
|
||||||
options.push({
|
options.push({
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export const AppThemeProvider: React.FC<{ children: React.ReactNode }> = ({ chil
|
||||||
const savedTheme = localStorage.getItem("theme");
|
const savedTheme = localStorage.getItem("theme");
|
||||||
const isValidTheme = (theme: string | null): theme is ThemeMode =>
|
const isValidTheme = (theme: string | null): theme is ThemeMode =>
|
||||||
theme === "light" || theme === "dark" || theme === "system";
|
theme === "light" || theme === "dark" || theme === "system";
|
||||||
const [mode, setMode] = useState<ThemeMode>(isValidTheme(savedTheme) ? savedTheme : "system");
|
const [mode, setMode] = useState<ThemeMode>(isValidTheme(savedTheme) ? savedTheme : "dark");
|
||||||
|
|
||||||
const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
|
const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
|
||||||
const resolvedMode: PaletteMode = mode === "system" ? (prefersDark ? "dark" : "light") : mode;
|
const resolvedMode: PaletteMode = mode === "system" ? (prefersDark ? "dark" : "light") : mode;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,20 @@ const baseTheme: ThemeOptions = {
|
||||||
paper: '#f5f5f5',
|
paper: '#f5f5f5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
typography: {
|
||||||
|
fontFamily: [
|
||||||
|
"Open Sans",
|
||||||
|
"-apple-system",
|
||||||
|
"BlinkMacSystemFont",
|
||||||
|
'"Segoe UI"',
|
||||||
|
'"Helvetica Neue"',
|
||||||
|
"Arial",
|
||||||
|
"sans-serif",
|
||||||
|
'"Apple Color Emoji"',
|
||||||
|
'"Segoe UI Emoji"',
|
||||||
|
'"Segoe UI Symbol"'
|
||||||
|
].join(",")
|
||||||
|
}
|
||||||
// Add other options if needed
|
// Add other options if needed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,14 +73,19 @@ export default function AccessObject(props: Props) {
|
||||||
const [teamsTotal, setTeamsTotal] = useState<number>(0);
|
const [teamsTotal, setTeamsTotal] = useState<number>(0);
|
||||||
const [devicesOffset, setDevicesOffset] = useState<number>(0);
|
const [devicesOffset, setDevicesOffset] = useState<number>(0);
|
||||||
const [devicesTotal, setDevicesTotal] = useState<number>(0);
|
const [devicesTotal, setDevicesTotal] = useState<number>(0);
|
||||||
|
const [prevAs, setPrevAs] = useState("")
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
|
const close = (restoreAs?: boolean) => {
|
||||||
|
if(restoreAs){
|
||||||
|
dispatch({ key: "as", value: prevAs });
|
||||||
|
}
|
||||||
closeDialogCallback();
|
closeDialogCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAccess = (key: number | string, name: string, url: string, useImitation = false) => {
|
const getAccess = (key: number | string, name: string, url: string, useImitation = false) => {
|
||||||
user.settings.useTeam = false;
|
user.settings.useTeam = false;
|
||||||
dispatch({ key: "as", value: "" });
|
//dispatch({ key: "as", value: "" });
|
||||||
userAPI.updateUser(user.id(), user.protobuf()).then(() => {
|
userAPI.updateUser(user.id(), user.protobuf()).then(() => {
|
||||||
info("Will no longer view as team by default");
|
info("Will no longer view as team by default");
|
||||||
permissionAPI
|
permissionAPI
|
||||||
|
|
@ -258,6 +263,12 @@ export default function AccessObject(props: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen && (prevSearchValue !== searchValue || prevKind !== kind || isOpen !== prevOpen)) {
|
if (isOpen && (prevSearchValue !== searchValue || prevKind !== kind || isOpen !== prevOpen)) {
|
||||||
|
//if the user is viewing as a team record who it was
|
||||||
|
if(as){
|
||||||
|
setPrevAs(as)
|
||||||
|
}
|
||||||
|
//change as so the user is viewing as themself
|
||||||
|
dispatch({ key: "as", value: "" });
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
|
@ -350,12 +361,12 @@ export default function AccessObject(props: Props) {
|
||||||
return (
|
return (
|
||||||
<ResponsiveDialog
|
<ResponsiveDialog
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
onClose={close}
|
onClose={() => close(true)}
|
||||||
aria-labelledby="access-object-dialog"
|
aria-labelledby="access-object-dialog"
|
||||||
fullScreen>
|
fullScreen>
|
||||||
<AppBar position="sticky">
|
<AppBar position="sticky">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<IconButton edge="start" color="inherit" onClick={close} aria-label="close">
|
<IconButton edge="start" color="inherit" onClick={() => close(true)} aria-label="close">
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" noWrap>
|
<Typography variant="h6" noWrap>
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ export default defineConfig({
|
||||||
outDir: './build',
|
outDir: './build',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
minify: false,
|
minify: false,
|
||||||
|
target: 'esnext'
|
||||||
},
|
},
|
||||||
esbuild: {
|
esbuild: {
|
||||||
keepNames: true, // Prevent function name mangling
|
keepNames: true, // Prevent function name mangling
|
||||||
}
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||