Merge branch 'master' into i2c_detect
This commit is contained in:
commit
5127a78cb0
25 changed files with 619 additions and 74 deletions
|
|
@ -303,8 +303,8 @@ deploy:development:
|
||||||
- "eval $(ssh-agent -s)"
|
- "eval $(ssh-agent -s)"
|
||||||
- ssh -V
|
- ssh -V
|
||||||
- cat /etc/os-release
|
- cat /etc/os-release
|
||||||
- "echo DEPLOY_TARGET=${DEPLOY_TARGET}"
|
# - "echo DEPLOY_TARGET=${DEPLOY_TARGET}"
|
||||||
- "echo DOCKER_HOST=${DOCKER_HOST}"
|
# - "echo DOCKER_HOST=${DOCKER_HOST}"
|
||||||
- "echo \"$DEVELOPMENT_SSH_PRIV_KEY\" | tr -d '\r' | ssh-add - "
|
- "echo \"$DEVELOPMENT_SSH_PRIV_KEY\" | tr -d '\r' | ssh-add - "
|
||||||
# - "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}"
|
||||||
|
|
@ -316,3 +316,98 @@ deploy:development:
|
||||||
- docker stack deploy --with-registry-auth -c docker-compose.dev.yml webui
|
- docker stack deploy --with-registry-auth -c docker-compose.dev.yml webui
|
||||||
when: manual
|
when: manual
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
|
|
||||||
|
dependencies:streamline:
|
||||||
|
image: node:$NODE_VERSION
|
||||||
|
stage: dependencies
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- streamline
|
||||||
|
# before_script:
|
||||||
|
# - npm -g config set user root
|
||||||
|
script:
|
||||||
|
- npm ci
|
||||||
|
cache:
|
||||||
|
key:
|
||||||
|
files:
|
||||||
|
- package-lock.json
|
||||||
|
paths:
|
||||||
|
- node_modules
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 hour
|
||||||
|
paths:
|
||||||
|
- node_modules/
|
||||||
|
|
||||||
|
# test:
|
||||||
|
# image: node:$NODE_VERSION
|
||||||
|
# stage: setup
|
||||||
|
# dependencies:
|
||||||
|
# - dependencies:production
|
||||||
|
# only:
|
||||||
|
# - master
|
||||||
|
# before_script:
|
||||||
|
# - npm run lint
|
||||||
|
# script:
|
||||||
|
# - CI=true npm run test
|
||||||
|
|
||||||
|
setup:streamline:
|
||||||
|
image: node:$NODE_VERSION
|
||||||
|
stage: setup
|
||||||
|
dependencies:
|
||||||
|
- dependencies:streamline
|
||||||
|
only:
|
||||||
|
- streamline
|
||||||
|
script:
|
||||||
|
- npm run build:streamline
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 hour
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
|
||||||
|
build:streamline:
|
||||||
|
image: docker:20.10.21
|
||||||
|
stage: build
|
||||||
|
dependencies:
|
||||||
|
- setup:streamline
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_TLS_CERTDIR: ""
|
||||||
|
services:
|
||||||
|
- docker:20.10.21-dind
|
||||||
|
only:
|
||||||
|
- streamline
|
||||||
|
before_script:
|
||||||
|
- docker login -u ${CI_REGISTRY_USER} -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
|
||||||
|
script:
|
||||||
|
- docker build -t ${CI_REGISTRY}/brandx/bxt-app/streamline:${CI_COMMIT_SHA} .
|
||||||
|
- docker tag ${CI_REGISTRY}/brandx/bxt-app/streamline:${CI_COMMIT_SHA} ${CI_REGISTRY}/brandx/bxt-app/streamline:latest
|
||||||
|
- docker push ${CI_REGISTRY}/brandx/bxt-app/streamline:${CI_COMMIT_SHA}
|
||||||
|
- docker push ${CI_REGISTRY}/brandx/bxt-app/streamline:latest
|
||||||
|
|
||||||
|
deploy:streamline:
|
||||||
|
image: docker:20.10.21
|
||||||
|
stage: deploy
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_HOST: ssh://deploy@streamline.adaptiveagriculture.ca
|
||||||
|
DEPLOY_IMAGE: ${CI_REGISTRY}/brandx/bxt-app/streamline:${CI_COMMIT_SHA}
|
||||||
|
DEPLOY_TARGET: deploy@streamline.adaptiveagriculture.ca
|
||||||
|
only:
|
||||||
|
- streamline
|
||||||
|
environment:
|
||||||
|
name: Streamline
|
||||||
|
url: https://streamline.adaptiveagriculture.ca
|
||||||
|
before_script: &deploy_tunnel
|
||||||
|
- "apk --update add openssh-client"
|
||||||
|
- "eval $(ssh-agent -s)"
|
||||||
|
- "echo \"$STREAMLINE_SSH_PRIV_KEY\" | tr -d '\r' | ssh-add - > /dev/null"
|
||||||
|
# - "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}"
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- chmod 700 ~/.ssh
|
||||||
|
- ssh -o StrictHostKeyChecking=no ${DEPLOY_TARGET}
|
||||||
|
script:
|
||||||
|
- docker login -u ${CI_REGISTRY_USER} -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
|
||||||
|
- docker stack deploy --with-registry-auth -c docker-compose.streamline.yml webui
|
||||||
|
when: manual
|
||||||
|
allow_failure: false
|
||||||
33
docker-compose.streamline.yml
Normal file
33
docker-compose.streamline.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
version: "3.6"
|
||||||
|
|
||||||
|
services:
|
||||||
|
webui:
|
||||||
|
image: ${DEPLOY_IMAGE}
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
stop_signal: SIGQUIT
|
||||||
|
deploy:
|
||||||
|
update_config:
|
||||||
|
order: start-first
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "localhost:8080/health"]
|
||||||
|
interval: 1m
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
labels:
|
||||||
|
com.datadoghq.ad.logs: '[{"source": "nginx", "service": "webui"}]'
|
||||||
|
traefik.docker.network: "backend"
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.webui.rule: "Host(`streamline.adaptiveagriculture.ca`)"
|
||||||
|
traefik.frontend.redirect.regex: "^https?://streamline.adaptiveagriculture.ca/(.*)"
|
||||||
|
traefik.frontend.redirect.replacement: "https://streamline.adaptiveagriculture.ca/$${1}"
|
||||||
|
traefik.backend: "www"
|
||||||
|
traefik.http.services.webui.loadbalancer.server.port: "80"
|
||||||
|
traefik.protocol: "http"
|
||||||
|
dns:
|
||||||
|
- 69.27.101.6
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
external: true
|
||||||
|
|
@ -63,6 +63,14 @@ http {
|
||||||
|
|
||||||
expires $cache_expires;
|
expires $cache_expires;
|
||||||
|
|
||||||
|
location = /update {
|
||||||
|
return 302 /update.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /update.html {
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
# Redirect old /index.html requests to new file
|
# Redirect old /index.html requests to new file
|
||||||
location = /index.html {
|
location = /index.html {
|
||||||
return 301 /indexV2.html;
|
return 301 /indexV2.html;
|
||||||
|
|
@ -79,7 +87,6 @@ http {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Healthcheck
|
# Healthcheck
|
||||||
server {
|
server {
|
||||||
listen 8080;
|
listen 8080;
|
||||||
|
|
|
||||||
71
package-lock.json
generated
71
package-lock.json
generated
|
|
@ -6374,10 +6374,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -6900,10 +6901,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
|
@ -7616,6 +7618,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
|
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/decode-uri-component": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/decompress-response": {
|
"node_modules/decompress-response": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||||
|
|
@ -8370,10 +8381,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/filelist/node_modules/brace-expansion": {
|
"node_modules/filelist/node_modules/brace-expansion": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -8402,6 +8414,18 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filter-obj": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/find-root": {
|
"node_modules/find-root": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||||
|
|
@ -10923,6 +10947,23 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/query-string": {
|
||||||
|
"version": "9.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-9.2.1.tgz",
|
||||||
|
"integrity": "sha512-3jTGGLRzlhu/1ws2zlr4Q+GVMLCQTLFOj8CMX5x44cdZG9FQE07x2mQhaNxaKVPNmIDu0mvJ/cEwtY7Pim7hqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"decode-uri-component": "^0.4.1",
|
||||||
|
"filter-obj": "^5.1.0",
|
||||||
|
"split-on-first": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue": {
|
"node_modules/queue": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
||||||
|
|
@ -12300,6 +12341,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/splaytree-ts/-/splaytree-ts-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/splaytree-ts/-/splaytree-ts-1.0.2.tgz",
|
||||||
"integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA=="
|
"integrity": "sha512-0kGecIZNIReCSiznK3uheYB8sbstLjCZLiwcQwbmLhgHJj2gz6OnSPkVzJQCMnmEz1BQ4gPK59ylhBoEWOhGNA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/split-on-first": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/split-string": {
|
"node_modules/split-string": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"start": "VITE_AUTH0_CLIENT_ID=5pUCkl2SfogkWmM244UDcLEUOp8EFdHd VITE_AUTH0_AUDIENCE=api.brandxtech.ca VITE_APP_API_URL=https://api.brandxtech.ca/v1 VITE_APP_WS_URL=ws://api.brandxtech.ca/v1/live vite",
|
"start": "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-streamline": "VITE_AUTH0_CLIENT_ID=HwUV0hHNdVvU96zuMBTAU8i7nFdwwgIX VITE_APP_API_URL=https://streamline.api.adaptiveagriculture.ca/v1 VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=streamline.api.adaptiveagriculture.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",
|
"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 .",
|
||||||
|
|
@ -14,6 +15,7 @@
|
||||||
"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: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_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_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": "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: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:streamline": "VITE_AUTH0_CLIENT_ID=HwUV0hHNdVvU96zuMBTAU8i7nFdwwgIX VITE_AUTH0_CLIENT_DOMAIN=brandxtech.auth0.com VITE_AUTH0_AUDIENCE=streamline.api.adaptiveagriculture.ca VITE_APP_API_URL=https://streamline.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_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"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
BIN
public/Streamline/favicon-16x16.png
Normal file
BIN
public/Streamline/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/Streamline/favicon-32x32.png
Normal file
BIN
public/Streamline/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/Streamline/favicon.ico
Normal file
BIN
public/Streamline/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
37
public/Streamline/manifest.json
Normal file
37
public/Streamline/manifest.json
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "Streamline",
|
||||||
|
"short_name": "Streamline",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "android-chrome-256x256.png",
|
||||||
|
"sizes": "256x256",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "maskable_icon.png",
|
||||||
|
"sizes": "196x196",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"theme_color": "#272727",
|
||||||
|
"background_color": "#303030",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "any"
|
||||||
|
}
|
||||||
BIN
public/Streamline/stream-logo.png
Normal file
BIN
public/Streamline/stream-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
44
public/update.html
Normal file
44
public/update.html
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Updating...</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
padding: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Updating App…</h1>
|
||||||
|
<p>Please wait while we refresh your version.</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function clean() {
|
||||||
|
// Unregister all SWs
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const regs = await navigator.serviceWorker.getRegistrations();
|
||||||
|
for (const reg of regs) {
|
||||||
|
await reg.unregister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all caches
|
||||||
|
if ('caches' in window) {
|
||||||
|
const keys = await caches.keys();
|
||||||
|
for (const key of keys) {
|
||||||
|
await caches.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hard reload
|
||||||
|
location.href = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
clean();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
src/assets/whitelabels/Streamline/stream-logo.png
Normal file
BIN
src/assets/whitelabels/Streamline/stream-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
|
|
@ -414,7 +414,7 @@ export default function BinComponents(props: Props) {
|
||||||
)}
|
)}
|
||||||
<ListItemText inset={cIcon === undefined}>
|
<ListItemText inset={cIcon === undefined}>
|
||||||
<Grid container direction="row" justifyContent="space-between">
|
<Grid container direction="row" justifyContent="space-between">
|
||||||
<Grid >{comp.name() + " " + comp.addressDescription(devMap.get(device)?.settings.product)}</Grid>
|
<Grid >{comp.name() + " - " + comp.addressDescription(devMap.get(device)?.settings.product)}</Grid>
|
||||||
<Grid >
|
<Grid >
|
||||||
{components?.get(comp.key()) !== undefined ? (
|
{components?.get(comp.key()) !== undefined ? (
|
||||||
<CheckBoxIcon />
|
<CheckBoxIcon />
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ const useStyles = makeStyles((theme: Theme) => {
|
||||||
},
|
},
|
||||||
stickyHeader: {
|
stickyHeader: {
|
||||||
position: "sticky",
|
position: "sticky",
|
||||||
backgroundColor: getThemeType() === "light" ? "rgb(245, 245, 245)" : "rgb(40, 40, 40)",
|
// backgroundColor: getThemeType() === "light" ? "rgb(245, 245, 245)" : "rgb(40, 40, 40)",
|
||||||
top: 0,
|
top: 0,
|
||||||
zIndex: 300 //giving a really high z-index to make sure nothing is in front of it
|
zIndex: 300 //giving a really high z-index to make sure nothing is in front of it
|
||||||
}
|
}
|
||||||
|
|
@ -165,6 +165,7 @@ export default function ResponsiveTable<T extends Object>(props: Props<T>) {
|
||||||
const saved = localStorage.getItem(rowKey);
|
const saved = localStorage.getItem(rowKey);
|
||||||
return saved ? JSON.parse(saved) : [];
|
return saved ? JSON.parse(saved) : [];
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem(rowKey, JSON.stringify(rowWidths));
|
localStorage.setItem(rowKey, JSON.stringify(rowWidths));
|
||||||
}, [rowWidths])
|
}, [rowWidths])
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export const appendToUrl = (appendage: number | string) => {
|
||||||
|
|
||||||
export default function Router() {
|
export default function Router() {
|
||||||
|
|
||||||
const { isAuthenticated, loginWithRedirect, isLoading } = useAuth0();
|
const { /*isAuthenticated, loginWithRedirect,*/ isLoading } = useAuth0();
|
||||||
const whiteLabel = getWhitelabel();
|
const whiteLabel = getWhitelabel();
|
||||||
const [{ user }] = useGlobalState();
|
const [{ user }] = useGlobalState();
|
||||||
|
|
||||||
|
|
@ -283,12 +283,12 @@ export default function Router() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) return null;
|
if (isLoading) return null;
|
||||||
if (!isAuthenticated) {
|
// if (!isAuthenticated) {
|
||||||
loginWithRedirect()
|
// loginWithRedirect()
|
||||||
return (
|
// return (
|
||||||
null
|
// null
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
|
||||||
function ErrorFallback({ error }: { error: Error }) {
|
function ErrorFallback({ error }: { error: Error }) {
|
||||||
return <div>Something went wrong: {error.stack}</div>;
|
return <div>Something went wrong: {error.stack}</div>;
|
||||||
|
|
@ -302,6 +302,7 @@ export default function Router() {
|
||||||
|
|
||||||
{/* Redirects */}
|
{/* Redirects */}
|
||||||
<Route path="/callback" element={<Navigate to="/" />} />
|
<Route path="/callback" element={<Navigate to="/" />} />
|
||||||
|
{/* <Route path="/login" element={<Login />} /> */}
|
||||||
{/* <Route path="/team/:teamID" element={<Navigate to="/teams/:teamID" />} /> */}
|
{/* <Route path="/team/:teamID" element={<Navigate to="/teams/:teamID" />} /> */}
|
||||||
{/* <Route path="/device/:deviceID" element={<Navigate to="/devices/:devicesID" />} /> */}
|
{/* <Route path="/device/:deviceID" element={<Navigate to="/devices/:devicesID" />} /> */}
|
||||||
<Route path="" element={<Navigate to={whiteLabel.homePage ? whiteLabel.homePage : "/devices"} />} />
|
<Route path="" element={<Navigate to={whiteLabel.homePage ? whiteLabel.homePage : "/devices"} />} />
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import {
|
||||||
// isBXT,
|
// isBXT,
|
||||||
IsMiVent,
|
IsMiVent,
|
||||||
IsOmniAir,
|
IsOmniAir,
|
||||||
|
IsStreamline,
|
||||||
} from "services/whiteLabel";
|
} from "services/whiteLabel";
|
||||||
import MiningIcon from "products/ventilation/MiningIcon";
|
import MiningIcon from "products/ventilation/MiningIcon";
|
||||||
import { useAuth0 } from "@auth0/auth0-react";
|
import { useAuth0 } from "@auth0/auth0-react";
|
||||||
|
|
@ -156,11 +157,12 @@ export default function SideNavigator(props: Props) {
|
||||||
const authenticatedSideMenu = () => {
|
const authenticatedSideMenu = () => {
|
||||||
const isMiVent = IsMiVent();
|
const isMiVent = IsMiVent();
|
||||||
const isAg = IsAdaptiveAgriculture()
|
const isAg = IsAdaptiveAgriculture()
|
||||||
|
const isStreamline = IsStreamline()
|
||||||
const isMiPCA = IsOmniAir()
|
const isMiPCA = IsOmniAir()
|
||||||
const isAdCon = IsAdCon()
|
const isAdCon = IsAdCon()
|
||||||
return (
|
return (
|
||||||
<List className={classes.list} component="nav">
|
<List className={classes.list} component="nav">
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
{(isAg || isStreamline || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Visual Farm" placement="right">
|
<Tooltip title="Visual Farm" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
id="tour-visual-farm"
|
id="tour-visual-farm"
|
||||||
|
|
@ -202,7 +204,7 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
{(isAg || isStreamline || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Contracts" placement="right">
|
<Tooltip title="Contracts" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
id="tour-contracts"
|
id="tour-contracts"
|
||||||
|
|
@ -216,7 +218,7 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
{(isAg || isStreamline || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Bins" placement="right">
|
<Tooltip title="Bins" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
id="tour-dashboard"
|
id="tour-dashboard"
|
||||||
|
|
@ -258,7 +260,7 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
{(isAg || isStreamline || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="Transactions" placement="right">
|
<Tooltip title="Transactions" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
id="tour-transactions"
|
id="tour-transactions"
|
||||||
|
|
@ -350,7 +352,7 @@ export default function SideNavigator(props: Props) {
|
||||||
{open && <ListItemText primary="Users" />}
|
{open && <ListItemText primary="Users" />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{(isAg || user.hasFeature("admin")) && (
|
{(isAg || isStreamline || user.hasFeature("admin")) && (
|
||||||
<Tooltip title="My Fields" placement="right">
|
<Tooltip title="My Fields" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
id="tour-field-list"
|
id="tour-field-list"
|
||||||
|
|
@ -434,7 +436,7 @@ export default function SideNavigator(props: Props) {
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{isAg &&
|
{(isAg || isStreamline) &&
|
||||||
<Tooltip title="Marketplace" placement="right">
|
<Tooltip title="Marketplace" placement="right">
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
id="tour-marketplace"
|
id="tour-marketplace"
|
||||||
|
|
@ -475,7 +477,7 @@ export default function SideNavigator(props: Props) {
|
||||||
{open && <ListItemText primary="Case New Holland" />}
|
{open && <ListItemText primary="Case New Holland" />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
</List>
|
</List>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,8 @@ export default function Devices() {
|
||||||
const [limit, setLimit] = useState(10);
|
const [limit, setLimit] = useState(10);
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
|
|
||||||
|
const [{ user }] = useGlobalState()
|
||||||
|
|
||||||
const [order, setOrder] = useState<"asc" | "desc">(() => {
|
const [order, setOrder] = useState<"asc" | "desc">(() => {
|
||||||
// Load from sessionStorage on initial render
|
// Load from sessionStorage on initial render
|
||||||
const savedOrder = sessionStorage.getItem('order');
|
const savedOrder = sessionStorage.getItem('order');
|
||||||
|
|
@ -585,6 +587,7 @@ export default function Devices() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const provisionButton= () => {
|
const provisionButton= () => {
|
||||||
|
if (!user.allowedTo("provision")) return null
|
||||||
return (
|
return (
|
||||||
<Tooltip title="Provision Device">
|
<Tooltip title="Provision Device">
|
||||||
<IconButton onClick={openProvisionDialog} aria-label="Provision Device">
|
<IconButton onClick={openProvisionDialog} aria-label="Provision Device">
|
||||||
|
|
|
||||||
53
src/pages/Login.tsx
Normal file
53
src/pages/Login.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { RedirectLoginOptions, useAuth0 } from "@auth0/auth0-react";
|
||||||
|
// import { useAuth } from "hooks";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useLocation } from "react-router";
|
||||||
|
// import Loading from "./Loading";
|
||||||
|
import LoadingScreen from "app/LoadingScreen";
|
||||||
|
|
||||||
|
// interface Props {
|
||||||
|
// prevPath?: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
// const { prevPath } = props;
|
||||||
|
const location = useLocation();
|
||||||
|
const { loginWithRedirect } = useAuth0();
|
||||||
|
|
||||||
|
// const setRouteBeforeLogin = useCallback((): Promise<string> => {
|
||||||
|
// return new Promise(function(resolve) {
|
||||||
|
// localStorage.setItem("routeBeforeLogin", prevPath ? prevPath : "/");
|
||||||
|
// return resolve("success");
|
||||||
|
// });
|
||||||
|
// }, [prevPath]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const options: RedirectLoginOptions = {
|
||||||
|
authorizationParams: {} // Initialize here
|
||||||
|
};
|
||||||
|
|
||||||
|
if (location && location.search) {
|
||||||
|
let parsed = queryString.parse(location.search);
|
||||||
|
|
||||||
|
if (parsed.signup === "true") {
|
||||||
|
// if (options.authorizationParams) options.authorizationParams.screen_hint = "signup"; // 'signup' tells Auth0 to show the signup tab
|
||||||
|
options.authorizationParams!.screen_hint = "signup"; // 'signup' tells Auth0 to show the signup tab
|
||||||
|
options.authorizationParams!.prompt = "login";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.email && parsed.email !== "") {
|
||||||
|
// if (options.authorizationParams) options.authorizationParams.login_hint = parsed.email.toString(); // prefill email
|
||||||
|
options.authorizationParams!.login_hint = parsed.email.toString(); // prefill email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setRouteBeforeLogin().finally(() => {
|
||||||
|
console.log(options.authorizationParams)
|
||||||
|
loginWithRedirect(options);
|
||||||
|
// });
|
||||||
|
}, [location, loginWithRedirect]);
|
||||||
|
|
||||||
|
// return <LoadingScreen fullViewport={true} />;
|
||||||
|
return <LoadingScreen message="login redirect" />;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Container, SxProps, Theme } from "@mui/material";
|
import { Container, SxProps, Theme } from "@mui/material";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useMobile } from "hooks";
|
// import { useMobile } from "hooks";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
|
@ -44,11 +44,11 @@ interface Props extends PropsWithChildren{
|
||||||
|
|
||||||
export const PageContainer: React.FunctionComponent<Props> = (props: Props) => {
|
export const PageContainer: React.FunctionComponent<Props> = (props: Props) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const isMobile = useMobile();
|
// const isMobile = useMobile();
|
||||||
const { children, fullViewport, isCenterCenter, sx } = props;
|
const { children, fullViewport, isCenterCenter, sx } = props;
|
||||||
// let fullViewport = false
|
// let fullViewport = false
|
||||||
// let isCenterCenter = false
|
// let isCenterCenter = false
|
||||||
const spacing = props.spacing;
|
const spacing = props.spacing ? props.spacing : 2;
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,55 @@
|
||||||
import { useAuth0 } from "@auth0/auth0-react";
|
import { RedirectLoginOptions, useAuth0 } from "@auth0/auth0-react";
|
||||||
|
import queryString from "query-string";
|
||||||
import { PropsWithChildren, useEffect } from "react";
|
import { PropsWithChildren, useEffect } from "react";
|
||||||
|
|
||||||
interface Props extends PropsWithChildren<any>{
|
interface Props extends PropsWithChildren<any>{
|
||||||
setToken: React.Dispatch<React.SetStateAction<string | undefined>>
|
setToken: React.Dispatch<React.SetStateAction<string | undefined>>
|
||||||
|
prevPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AuthWrapper(props: Props) {
|
export default function AuthWrapper(props: Props) {
|
||||||
const { children, setToken } = props;
|
const { children, setToken } = props;
|
||||||
const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently } = useAuth0();
|
const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently } = useAuth0();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isAuthenticated && !isLoading) {
|
|
||||||
loginWithRedirect();
|
|
||||||
}
|
|
||||||
}, [isAuthenticated, loginWithRedirect, isLoading]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isAuthenticated) getAccessTokenSilently().then(t => {
|
|
||||||
if (t.length < 1) {
|
|
||||||
loginWithRedirect()
|
|
||||||
}
|
|
||||||
setToken(t)
|
|
||||||
}).catch(() => {
|
|
||||||
// No token, go back to login
|
|
||||||
loginWithRedirect()
|
|
||||||
})
|
|
||||||
}, [setToken, isAuthenticated])
|
|
||||||
|
|
||||||
return children;
|
useEffect(() => {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
|
||||||
|
const isManualLoginPage = url.pathname.includes("/login");
|
||||||
|
|
||||||
|
if (isManualLoginPage) {
|
||||||
|
const options: RedirectLoginOptions = {
|
||||||
|
authorizationParams: {} // Initialize here
|
||||||
|
};
|
||||||
|
|
||||||
|
let parsed = queryString.parse(url.search);
|
||||||
|
if (parsed.signup === "true") {
|
||||||
|
options.authorizationParams!.screen_hint = "signup";
|
||||||
|
options.authorizationParams!.prompt = "login";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.email && parsed.email !== "") {
|
||||||
|
options.authorizationParams!.login_hint = parsed.email.toString(); // prefill email
|
||||||
|
}
|
||||||
|
|
||||||
|
loginWithRedirect(options)
|
||||||
|
|
||||||
|
} else if (!isAuthenticated && !isLoading) {
|
||||||
|
loginWithRedirect();
|
||||||
|
}
|
||||||
|
}, [isAuthenticated, loginWithRedirect, isLoading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAuthenticated) getAccessTokenSilently().then(t => {
|
||||||
|
if (t.length < 1) {
|
||||||
|
loginWithRedirect()
|
||||||
|
}
|
||||||
|
setToken(t)
|
||||||
|
}).catch(() => {
|
||||||
|
// No token, go back to login
|
||||||
|
loginWithRedirect()
|
||||||
|
})
|
||||||
|
}, [setToken, isAuthenticated])
|
||||||
|
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import AeroGrowLightLogo from "../assets/whitelabels/AeroGrow/lightLogo.png";
|
||||||
import MiVentLightLogo from "../assets/whitelabels/MiVent/lightLogo.png";
|
import MiVentLightLogo from "../assets/whitelabels/MiVent/lightLogo.png";
|
||||||
// import OmniAirLogo from "../assets/whitelabels/OmniAir/OmniAirLogo.png";
|
// import OmniAirLogo from "../assets/whitelabels/OmniAir/OmniAirLogo.png";
|
||||||
import MiPCALogo from "../assets/whitelabels/OmniAir/MiPCALogo.png";
|
import MiPCALogo from "../assets/whitelabels/OmniAir/MiPCALogo.png";
|
||||||
|
import StreamlineLogo from "../assets/whitelabels/Streamline/stream-logo.png"
|
||||||
// import { green, yellow } from "@mui/material/colors";
|
// import { green, yellow } from "@mui/material/colors";
|
||||||
|
|
||||||
const protips: string[] = [
|
const protips: string[] = [
|
||||||
|
|
@ -111,6 +112,28 @@ const BXT_WHITE_LABEL: WhiteLabel = {
|
||||||
homePage: "/devices"
|
homePage: "/devices"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STREAMLINE_WHITE_LABEL: WhiteLabel = {
|
||||||
|
name: "Streamline",
|
||||||
|
primaryColour: "#FFFF",
|
||||||
|
// primaryColour: "#0000FF",
|
||||||
|
secondaryColour: "yellow",
|
||||||
|
// secondaryColour: "#FFFF00",
|
||||||
|
// signatureColour: "#272727",
|
||||||
|
signatureColour: "#005bb0",
|
||||||
|
signatureAccentColour: "#fff",
|
||||||
|
auth0ClientId: import.meta.env.VITE_AUTH0_STREAMLINE_CLIENT_ID,
|
||||||
|
redirectOnLogout: false,
|
||||||
|
logoutRedirectTarget: "",
|
||||||
|
darkLogo: StreamlineLogo,
|
||||||
|
lightLogo: StreamlineLogo,
|
||||||
|
transparentLogoBG: false,
|
||||||
|
blacklist: [],
|
||||||
|
hotjarID: import.meta.env.REACT_APP_HOTJAR_ID_BXT,
|
||||||
|
docs: "Platform",
|
||||||
|
protips: protips,
|
||||||
|
homePage: "/devices"
|
||||||
|
};
|
||||||
|
|
||||||
export function isBXT(): boolean {
|
export function isBXT(): boolean {
|
||||||
return getName() === "Brand X Technologies";
|
return getName() === "Brand X Technologies";
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +180,14 @@ export function IsAdaptiveAgriculture(): boolean {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function IsStreamline(): boolean {
|
||||||
|
return (
|
||||||
|
getName() === "Streamline"
|
||||||
|
// window.location.origin.includes("staging") ||
|
||||||
|
// window.location.origin.includes("localhost")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const AEROGROW_WHITE_LABEL: WhiteLabel = {
|
const AEROGROW_WHITE_LABEL: WhiteLabel = {
|
||||||
name: "AeroGrow",
|
name: "AeroGrow",
|
||||||
primaryColour: "green",
|
primaryColour: "green",
|
||||||
|
|
@ -254,6 +285,7 @@ export function IsOmniAir(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
const whitelabels = new Map<string, WhiteLabel>([
|
const whitelabels = new Map<string, WhiteLabel>([
|
||||||
|
["streamline", STREAMLINE_WHITE_LABEL],
|
||||||
["adaptiveag", ADAPTIVE_AGRICULTURE_WHITE_LABEL],
|
["adaptiveag", ADAPTIVE_AGRICULTURE_WHITE_LABEL],
|
||||||
["adaptiveagriculture", ADAPTIVE_AGRICULTURE_WHITE_LABEL],
|
["adaptiveagriculture", ADAPTIVE_AGRICULTURE_WHITE_LABEL],
|
||||||
["brandxducks", BXT_WHITE_LABEL],
|
["brandxducks", BXT_WHITE_LABEL],
|
||||||
|
|
@ -276,6 +308,9 @@ 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("localhost")) {
|
||||||
|
return STREAMLINE_WHITE_LABEL;
|
||||||
|
}
|
||||||
if (window.location.origin.includes("staging") || import.meta.env.VITE_LOCAL_STAGING=='true') {
|
if (window.location.origin.includes("staging") || import.meta.env.VITE_LOCAL_STAGING=='true') {
|
||||||
return STAGING_WHITELABEL;
|
return STAGING_WHITELABEL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ export default function TeamActions(props: Props) {
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
isDialogOpen={openState.share}
|
isDialogOpen={openState.share}
|
||||||
closeDialogCallback={() => setOpenState({ ...openState, share: false })}
|
closeDialogCallback={() => setOpenState({ ...openState, share: false })}
|
||||||
|
useImitation={false}
|
||||||
/>
|
/>
|
||||||
<ObjectUsers
|
<ObjectUsers
|
||||||
scope={teamScope(key)}
|
scope={teamScope(key)}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { createContext, useContext, useMemo, useState } from "react";
|
import React, { createContext, useContext, useMemo, useState } from "react";
|
||||||
import { ThemeProvider, createTheme, PaletteMode } from "@mui/material/styles";
|
import { ThemeProvider, PaletteMode } from "@mui/material/styles";
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
import { getTheme } from "./theme";
|
import { getTheme } from "./theme";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { Avatar, Box, Button, Checkbox, Divider, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, PaletteColor, Theme, Tooltip, useTheme } from "@mui/material";
|
import { Avatar, Box, Button, Checkbox, Divider, ListItemIcon, ListItemText, Menu, MenuItem, PaletteColor, Theme, Tooltip, useTheme } from "@mui/material";
|
||||||
import { useGlobalState } from "../providers/StateContainer";
|
import { useGlobalState } from "../providers/StateContainer";
|
||||||
import { getSecondaryColour, getSignatureAccentColour } from "../services/whiteLabel";
|
import { getSecondaryColour, getSignatureAccentColour } from "../services/whiteLabel";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { LockOpen, Person, Lock, Settings, SupervisedUserCircle as TeamIcon, ExitToApp, PersonAdd } from "@mui/icons-material";
|
import { LockOpen, Person, Lock, Settings, SupervisedUserCircle as TeamIcon, ExitToApp, PersonAdd } from "@mui/icons-material";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import UserTeamName from "./UserTeamName";
|
import UserTeamName from "./UserTeamName";
|
||||||
import ThemeIcon from "../common/ThemeIcon";
|
|
||||||
import { useAuth0 } from "@auth0/auth0-react";
|
import { useAuth0 } from "@auth0/auth0-react";
|
||||||
import UserSettings from "./UserSettings";
|
import UserSettings from "./UserSettings";
|
||||||
import UserAvatar from "./UserAvatar";
|
import UserAvatar from "./UserAvatar";
|
||||||
|
|
@ -14,7 +13,7 @@ import { useNavigate } from "react-router-dom";
|
||||||
import TeamDialog from "teams/TeamDialog";
|
import TeamDialog from "teams/TeamDialog";
|
||||||
import AccessObject from "./AccessObject";
|
import AccessObject from "./AccessObject";
|
||||||
import { useSnackbar, useUserAPI } from "hooks";
|
import { useSnackbar, useUserAPI } from "hooks";
|
||||||
import { useThemeMode } from "theme/AppThemeProvider";
|
// import { useThemeMode } from "theme/AppThemeProvider";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
accessIcon: {
|
accessIcon: {
|
||||||
|
|
@ -76,7 +75,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
|
||||||
export default function UserMenu() {
|
export default function UserMenu() {
|
||||||
|
|
||||||
const { toggleMode } = useThemeMode()
|
// const { toggleMode } = useThemeMode()
|
||||||
const [{ user, team, as }, dispatch] = useGlobalState();
|
const [{ user, team, as }, dispatch] = useGlobalState();
|
||||||
const { loginWithRedirect } = useAuth0();
|
const { loginWithRedirect } = useAuth0();
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
@ -163,10 +162,6 @@ export default function UserMenu() {
|
||||||
<Lock className={classes.rightIcon} fontSize="small" />
|
<Lock className={classes.rightIcon} fontSize="small" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<IconButton onClick={/*toggleMode*/ () => {}} className={classes.rightIcon} aria-label="Toggle Theme">
|
|
||||||
<ThemeIcon />
|
|
||||||
</IconButton>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import {
|
||||||
Textsms as TextIcon,
|
Textsms as TextIcon,
|
||||||
Contrast,
|
Contrast,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { AppBar, Box, Button, Checkbox, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Radio, RadioGroup, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material";
|
import { AppBar, Box, Button, Checkbox, CircularProgress, Collapse, Divider, FormControl, FormControlLabel, FormGroup, FormHelperText, FormLabel, Grid2, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, MenuItem, Radio, RadioGroup, TextField, Theme, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, Typography } from "@mui/material";
|
||||||
import ResponsiveDialog from "common/ResponsiveDialog";
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
import UserAvatar from "./UserAvatar";
|
import UserAvatar from "./UserAvatar";
|
||||||
import { useGlobalState, useUserAPI } from "providers";
|
import { useGlobalState, useSnackbar, useUserAPI } from "providers";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { makeStyles } from "@mui/styles";
|
import { makeStyles } from "@mui/styles";
|
||||||
import { blue } from "@mui/material/colors";
|
import { blue } from "@mui/material/colors";
|
||||||
|
|
@ -26,8 +26,11 @@ import moment from "moment";
|
||||||
import { IconPicker } from "common/IconPicker";
|
import { IconPicker } from "common/IconPicker";
|
||||||
import { MuiTelInput } from "mui-tel-input";
|
import { MuiTelInput } from "mui-tel-input";
|
||||||
import { useThemeMode } from "theme/AppThemeProvider";
|
import { useThemeMode } from "theme/AppThemeProvider";
|
||||||
import ContractsIcon from "products/CommonIcons/contractIcon";
|
// import ContractsIcon from "products/CommonIcons/contractIcon";
|
||||||
|
import UnitsIcon from "@mui/icons-material/Category";
|
||||||
import { setThemeType } from "theme";
|
import { setThemeType } from "theme";
|
||||||
|
import { IsAdaptiveAgriculture } from "services/whiteLabel";
|
||||||
|
import { setDistanceUnit, setGrainUnit, setPressureUnit, setTemperatureUnit } from "utils";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => {
|
const useStyles = makeStyles((theme: Theme) => {
|
||||||
return ({
|
return ({
|
||||||
|
|
@ -71,14 +74,16 @@ export default function UserSettings(props: Props) {
|
||||||
const [sharing, setSharing] = useState(false)
|
const [sharing, setSharing] = useState(false)
|
||||||
const userAPI = useUserAPI();
|
const userAPI = useUserAPI();
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
const { success } = useSnackbar();
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [user, setUser] = useState<User>(globalState.user);
|
const [user, setUser] = useState<User>(globalState.user);
|
||||||
const [profileExpanded, setProfileExpanded] = useState<boolean>(false);
|
const [profileExpanded, setProfileExpanded] = useState<boolean>(false);
|
||||||
const [avatarExpanded, setAvatarExpanded] = useState<boolean>(false);
|
const [avatarExpanded, setAvatarExpanded] = useState<boolean>(false);
|
||||||
const [notificationsExpanded, setNotificationsExpanded] = useState<boolean>(false);
|
const [notificationsExpanded, setNotificationsExpanded] = useState<boolean>(false);
|
||||||
|
const [unitsExpanded, setUnitsExpanded] = useState<boolean>(false);
|
||||||
const [avatarUrl, setAvatarUrl] = useState<string>("");
|
const [avatarUrl, setAvatarUrl] = useState<string>("");
|
||||||
const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode)
|
// const [themeValue, setThemeValue] = useState<"light" | "dark" | "system">(themeMode.mode)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function changeIcon(icon: string | undefined) {
|
function changeIcon(icon: string | undefined) {
|
||||||
|
|
@ -99,11 +104,11 @@ export default function UserSettings(props: Props) {
|
||||||
.updateUser(user.id(), user.protobuf())
|
.updateUser(user.id(), user.protobuf())
|
||||||
.then(() => {
|
.then(() => {
|
||||||
dispatch({ key: "user", value: user });
|
dispatch({ key: "user", value: user });
|
||||||
// success("User settings successfully updated!");
|
success("User settings successfully updated!");
|
||||||
// setTemperatureUnit(user.settings.temperatureUnit);
|
setTemperatureUnit(user.settings.temperatureUnit);
|
||||||
// setPressureUnit(user.settings.pressureUnit);
|
setPressureUnit(user.settings.pressureUnit);
|
||||||
// setDistanceUnit(user.settings.distanceUnit);
|
setDistanceUnit(user.settings.distanceUnit);
|
||||||
// setGrainUnit(user.settings.grainUnit);
|
setGrainUnit(user.settings.grainUnit);
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
error("Error occurred while updating your profile");
|
error("Error occurred while updating your profile");
|
||||||
|
|
@ -362,6 +367,160 @@ export default function UserSettings(props: Props) {
|
||||||
setThemeType(theme)
|
setThemeType(theme)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changePressureUnit = (value: any) => {
|
||||||
|
let updatedUser = User.clone(user);
|
||||||
|
let pressureUnit: pond.PressureUnit;
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
pressureUnit = pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pressureUnit = pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pressureUnit = pond.PressureUnit.PRESSURE_UNIT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updatedUser.settings.pressureUnit = pressureUnit;
|
||||||
|
setUser(updatedUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeTemperatureUnit = (value: any) => {
|
||||||
|
let updatedUser = User.clone(user);
|
||||||
|
let temperatureUnit: pond.TemperatureUnit;
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
temperatureUnit = pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
temperatureUnit = pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
temperatureUnit = pond.TemperatureUnit.TEMPERATURE_UNIT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updatedUser.settings.temperatureUnit = temperatureUnit;
|
||||||
|
setUser(updatedUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeDistanceUnit = (value: any) => {
|
||||||
|
let updatedUser = User.clone(user);
|
||||||
|
let distanceUnit: pond.DistanceUnit;
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
distanceUnit = pond.DistanceUnit.DISTANCE_UNIT_FEET;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
distanceUnit = pond.DistanceUnit.DISTANCE_UNIT_METERS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
distanceUnit = pond.DistanceUnit.DISTANCE_UNIT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updatedUser.settings.distanceUnit = distanceUnit;
|
||||||
|
setUser(updatedUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeGrainUnit = (value: any) => {
|
||||||
|
let updatedUser = User.clone(user);
|
||||||
|
let grainUnit: pond.GrainUnit;
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
grainUnit = pond.GrainUnit.GRAIN_UNIT_BUSHELS;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
grainUnit = pond.GrainUnit.GRAIN_UNIT_WEIGHT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
grainUnit = pond.GrainUnit.GRAIN_UNIT_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updatedUser.settings.grainUnit = grainUnit;
|
||||||
|
setUser(updatedUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unitPreferences = () => {
|
||||||
|
const { pressureUnit, temperatureUnit, distanceUnit, grainUnit } = user.settings;
|
||||||
|
return (
|
||||||
|
<Grid2 container>
|
||||||
|
<Grid2 size={{ xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
id="pressureUnit"
|
||||||
|
name="pressureUnit"
|
||||||
|
label="Pressure Unit"
|
||||||
|
value={pressureUnit ? pressureUnit : pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER}
|
||||||
|
onChange={event => changePressureUnit(event.target.value)}
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
InputLabelProps={{ shrink: true }}>
|
||||||
|
<MenuItem value={pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS}>
|
||||||
|
Kilopascals (kPa)
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER}>
|
||||||
|
Inches of Water (iwg)
|
||||||
|
</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={{ xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
id="temperatureUnit"
|
||||||
|
name="temperatureUnit"
|
||||||
|
label="Temperature Unit"
|
||||||
|
value={
|
||||||
|
temperatureUnit ? temperatureUnit : pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS
|
||||||
|
}
|
||||||
|
onChange={event => changeTemperatureUnit(event.target.value)}
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
InputLabelProps={{ shrink: true }}>
|
||||||
|
<MenuItem value={pond.TemperatureUnit.TEMPERATURE_UNIT_CELSIUS}>Celsius (°C)</MenuItem>
|
||||||
|
<MenuItem value={pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT}>
|
||||||
|
Fahrenheit (°F)
|
||||||
|
</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={{ xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
id="distanceUnit"
|
||||||
|
name="distanceUnit"
|
||||||
|
label="Distance Unit"
|
||||||
|
value={distanceUnit ? distanceUnit : pond.DistanceUnit.DISTANCE_UNIT_METERS}
|
||||||
|
onChange={event => changeDistanceUnit(event.target.value)}
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
InputLabelProps={{ shrink: true }}>
|
||||||
|
<MenuItem value={pond.DistanceUnit.DISTANCE_UNIT_METERS}>Meters (m)</MenuItem>
|
||||||
|
<MenuItem value={pond.DistanceUnit.DISTANCE_UNIT_FEET}>Feet (ft)</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</Grid2>
|
||||||
|
{IsAdaptiveAgriculture() && (
|
||||||
|
<Grid2 size={{ xs: 12 }}>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
id="grainUnit"
|
||||||
|
name="grainUnit"
|
||||||
|
label="Grain Unit"
|
||||||
|
value={grainUnit ? grainUnit : pond.GrainUnit.GRAIN_UNIT_BUSHELS}
|
||||||
|
onChange={event => changeGrainUnit(event.target.value)}
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
InputLabelProps={{ shrink: true }}>
|
||||||
|
<MenuItem value={pond.GrainUnit.GRAIN_UNIT_BUSHELS}>Bushels (bu)</MenuItem>
|
||||||
|
<MenuItem value={pond.GrainUnit.GRAIN_UNIT_WEIGHT}>Tonnes (mT)</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
</Grid2>
|
||||||
|
)}
|
||||||
|
</Grid2>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const themeToggle = () => {
|
const themeToggle = () => {
|
||||||
return (
|
return (
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
|
|
@ -453,15 +612,15 @@ export default function UserSettings(props: Props) {
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
{/* <Divider />
|
<Divider />
|
||||||
|
|
||||||
<ListItem button onClick={() => setUnitsExpanded(!unitsExpanded)}>
|
<ListItemButton onClick={() => setUnitsExpanded(!unitsExpanded)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<UnitsIcon />
|
<UnitsIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary="Preferred Units" />
|
<ListItemText primary="Preferred Units" />
|
||||||
{unitsExpanded ? <ExpandLess /> : <ExpandMore />}
|
{unitsExpanded ? <ExpandLess /> : <ExpandMore />}
|
||||||
</ListItem>
|
</ListItemButton>
|
||||||
<Collapse in={unitsExpanded} timeout="auto" unmountOnExit>
|
<Collapse in={unitsExpanded} timeout="auto" unmountOnExit>
|
||||||
<List component="div">
|
<List component="div">
|
||||||
<ListItem>
|
<ListItem>
|
||||||
|
|
@ -472,7 +631,7 @@ export default function UserSettings(props: Props) {
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<Divider />
|
{/* <Divider />
|
||||||
|
|
||||||
<ListItem button onClick={() => setMapsExpanded(!mapsExpanded)}>
|
<ListItem button onClick={() => setMapsExpanded(!mapsExpanded)}>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue