merged in team share branch and update proto

This commit is contained in:
Carter 2025-06-18 16:19:29 -06:00
commit 78884da48c
25 changed files with 359 additions and 48 deletions

View file

@ -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

View 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

View file

@ -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;

21
package-lock.json generated
View file

@ -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"
@ -8370,10 +8372,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"
} }

View file

@ -7,13 +7,15 @@
"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 .",
"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_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"
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

44
public/update.html Normal file
View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View file

@ -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])

View file

@ -118,7 +118,9 @@ export default function UpgradeDevice(props: Props) {
}; };
const percent = () => { const percent = () => {
return Math.round((upgrade.status.offset / upgrade.status.size) * 100); let progress = Math.round((upgrade.status.offset / upgrade.status.size) * 100)
console.log(progress)
return progress;
}; };
const tooltip = () => { const tooltip = () => {
@ -243,7 +245,7 @@ export default function UpgradeDevice(props: Props) {
<UpdateIcon /> <UpdateIcon />
{upgrading() && ( {upgrading() && (
<CircularProgress <CircularProgress
// variant="static" variant="determinate"
value={percent()} value={percent()}
size={"2rem"} size={"2rem"}
className={classes.buttonProgress} className={classes.buttonProgress}

View file

@ -172,7 +172,7 @@ export default function GateFlowGraph(props: Props) {
const flowChart = () => { const flowChart = () => {
return ( return (
<Card raised style={{ padding: 10, marginBottom: 15 }}> <Card raised style={{ padding: 10, marginBottom: 15, marginTop: 15 }}>
<CardHeader <CardHeader
title={<Typography style={{ fontSize: 25, fontWeight: 650 }}>Mass Air Flow</Typography>} title={<Typography style={{ fontSize: 25, fontWeight: 650 }}>Mass Air Flow</Typography>}
subheader={ subheader={
@ -369,7 +369,7 @@ export default function GateFlowGraph(props: Props) {
<Grid container direction="row" spacing={2} className={classes.runtimeGrid}> <Grid container direction="row" spacing={2} className={classes.runtimeGrid}>
<Grid size={3}> <Grid size={3}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Approximate Runtime:</Grid> <Grid>Approximate Runtime:</Grid>
<Grid> <Grid>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
@ -381,7 +381,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
<Grid size={3}> <Grid size={3}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Cost to run PCA:</Grid> <Grid>Cost to run PCA:</Grid>
<Grid> <Grid>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
@ -396,7 +396,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
<Grid size={3}> <Grid size={3}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Cost to run APU:</Grid> <Grid>Cost to run APU:</Grid>
<Grid> <Grid>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>
@ -411,7 +411,7 @@ export default function GateFlowGraph(props: Props) {
</Grid> </Grid>
<Grid size={3}> <Grid size={3}>
<Card raised className={classes.calcCard}> <Card raised className={classes.calcCard}>
<Grid container direction="column" alignItems="center"> <Grid container width="100%" direction="column" alignItems="center">
<Grid>Total Cost:</Grid> <Grid>Total Cost:</Grid>
<Grid> <Grid>
<Typography style={{ fontWeight: 650, fontSize: 15 }}> <Typography style={{ fontWeight: 650, fontSize: 15 }}>

View file

@ -96,7 +96,7 @@ export class Bin {
} }
public empty(): boolean { public empty(): boolean {
return this.settings.inventory?.empty || this.settings.inventory?.grainBushels === 0; return this.settings.inventory?.empty || (this.settings.inventory !== undefined && this.settings.inventory !== null && this.settings.inventory.grainBushels < 5);
} }
public objectType(): pond.ObjectType { public objectType(): pond.ObjectType {

View file

@ -9,6 +9,7 @@ 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"; import DeviceSupport from "pages/DeviceSupport";
import Login from "pages/Login";
const DeviceHistory = lazy(() => import("pages/DeviceHistory")); const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
const DevicePage = lazy(() => import("pages/Device")); const DevicePage = lazy(() => import("pages/Device"));
@ -283,12 +284,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 +303,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"} />} />

View file

@ -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"

View file

@ -434,7 +434,7 @@ export default function DevicePage() {
} }
return ( return (
<PageContainer padding={isMobile ? 0 : 2}> <PageContainer spacing={isMobile ? 0 : 2}>
<Grid container justifyContent={"space-between"}> <Grid container justifyContent={"space-between"}>
<Grid> <Grid>
<SmartBreadcrumb deviceName={device.name()} /> <SmartBreadcrumb deviceName={device.name()} />

View file

@ -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">

View file

@ -115,17 +115,13 @@ export default function Gate(props: Props) {
}; };
useEffect(() => { useEffect(() => {
console.log(gateID)
let key = gateID; let key = gateID;
let kind = "gate"; let kind = "gate";
if (as) { if (as) {
key = as; key = as;
kind = "team"; kind = "team";
} }
console.log(key)
console.log(kind)
userAPI.getUser(user.id(), { key: key, kind: kind } as Scope).then(resp => { userAPI.getUser(user.id(), { key: key, kind: kind } as Scope).then(resp => {
console.log(resp)
setPermissions(resp.permissions); setPermissions(resp.permissions);
}); });
}, [as, gateID, userAPI, user]); }, [as, gateID, userAPI, user]);

53
src/pages/Login.tsx Normal file
View file

@ -0,0 +1,53 @@
import { RedirectLoginOptions, useAuth0 } from "@auth0/auth0-react";
// import { useAuth } from "hooks";
import queryString from "query-string";
import { useCallback, 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(props: Props) {
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
};
console.log(location)
if (location && location.search) {
let parsed = queryString.parse(location.search);
console.log(parsed.signup)
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
console.log(options)
}
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(() => {
loginWithRedirect(options);
});
}, [location, loginWithRedirect, setRouteBeforeLogin]);
// return <LoadingScreen fullViewport={true} />;
return <LoadingScreen message="login redirect" />;
}

View file

@ -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;
} }

View file

@ -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";

View file

@ -14,7 +14,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 +76,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 +163,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>
</> </>
); );
}; };