From ef6a83bb5b41c424137760f96b850021f3f018f1 Mon Sep 17 00:00:00 2001 From: Dylan Johnson Date: Wed, 22 Jan 2025 12:50:58 -0700 Subject: [PATCH] Base UI --- _old/Dockerfile | 6 +- _old/install/install.sh | 4 +- api/Dockerfile | 4 +- web/Dockerfile | 2 +- web/angular.json | 8 +- web/package-lock.json | 1236 ++++++++++++++--- web/package.json | 4 + web/src/app/about/about.component.html | 1 + web/src/app/about/about.component.scss | 0 web/src/app/about/about.component.spec.ts | 23 + web/src/app/about/about.component.ts | 12 + web/src/app/app.component.html | 314 +---- web/src/app/app.component.scss | 8 + web/src/app/app.component.ts | 2 + web/src/app/app.config.server.ts | 11 +- web/src/app/app.config.ts | 35 +- web/src/app/app.routes.ts | 70 +- .../app/dashboard/dashboard.component.html | 59 + .../app/dashboard/dashboard.component.scss | 15 + .../app/dashboard/dashboard.component.spec.ts | 23 + web/src/app/dashboard/dashboard.component.ts | 31 + .../recent-items/recent-items.component.html | 18 + .../recent-items/recent-items.component.scss | 8 + .../recent-items.component.spec.ts | 23 + .../recent-items/recent-items.component.ts | 68 + .../diagram-details.component.html | 1 + .../diagram-details.component.scss | 0 .../diagram-details.component.spec.ts | 23 + .../diagram-details.component.ts | 12 + .../investigation-main.component.html | 1 + .../investigation-main.component.scss | 0 .../investigation-main.component.spec.ts | 23 + .../investigation-main.component.ts | 12 + .../layout-login/layout-login.component.html | 27 + .../layout-login/layout-login.component.scss | 80 ++ .../layout-login.component.spec.ts | 23 + .../layout-login/layout-login.component.ts | 36 + web/src/app/layout/layout.component.html | 64 + web/src/app/layout/layout.component.scss | 83 ++ web/src/app/layout/layout.component.spec.ts | 23 + web/src/app/layout/layout.component.ts | 48 + web/src/app/login/login.component.html | 48 + web/src/app/login/login.component.scss | 18 + web/src/app/login/login.component.spec.ts | 23 + web/src/app/login/login.component.ts | 105 ++ .../main-diagram/main-diagram.component.html | 1 + .../main-diagram/main-diagram.component.scss | 0 .../main-diagram.component.spec.ts | 23 + .../main-diagram/main-diagram.component.ts | 12 + .../app/not-found/not-found.component.html | 1 + .../app/not-found/not-found.component.scss | 0 .../app/not-found/not-found.component.spec.ts | 23 + web/src/app/not-found/not-found.component.ts | 12 + .../password-restore.component.html | 1 + .../password-restore.component.scss | 0 .../password-restore.component.spec.ts | 23 + .../password-restore.component.ts | 12 + web/src/app/register/register.component.html | 1 + web/src/app/register/register.component.scss | 0 .../app/register/register.component.spec.ts | 23 + web/src/app/register/register.component.ts | 12 + web/src/app/services/auth-service.ts | 52 + .../services/authGuards/auth-guard-admin.ts | 0 .../services/authGuards/auth-guard-general.ts | 34 + web/src/app/services/config-service.ts | 55 + web/src/app/services/dashboard-service.ts | 19 + web/src/app/services/login.service.ts | 89 ++ web/src/app/services/storage-service.ts | 18 + web/src/app/tokens/index.ts | 3 + .../user-management.component.html | 1 + .../user-management.component.scss | 0 .../user-management.component.spec.ts | 23 + .../user-management.component.ts | 12 + web/src/assets/config.json | 24 + web/src/environments/environment.prod.ts | 27 + web/src/environments/environment.ts | 27 + web/src/index.html | 7 +- web/src/models/httpReponseContext.ts | 11 + web/src/models/userTokenModel.ts | 8 + web/src/styles.scss | 174 +++ web/src/tsconfig.app.json | 19 + web/src/tsconfig.spec.json | 20 + 82 files changed, 2857 insertions(+), 545 deletions(-) create mode 100644 web/src/app/about/about.component.html create mode 100644 web/src/app/about/about.component.scss create mode 100644 web/src/app/about/about.component.spec.ts create mode 100644 web/src/app/about/about.component.ts create mode 100644 web/src/app/dashboard/dashboard.component.html create mode 100644 web/src/app/dashboard/dashboard.component.scss create mode 100644 web/src/app/dashboard/dashboard.component.spec.ts create mode 100644 web/src/app/dashboard/dashboard.component.ts create mode 100644 web/src/app/dashboard/recent-items/recent-items.component.html create mode 100644 web/src/app/dashboard/recent-items/recent-items.component.scss create mode 100644 web/src/app/dashboard/recent-items/recent-items.component.spec.ts create mode 100644 web/src/app/dashboard/recent-items/recent-items.component.ts create mode 100644 web/src/app/diagram-details/diagram-details.component.html create mode 100644 web/src/app/diagram-details/diagram-details.component.scss create mode 100644 web/src/app/diagram-details/diagram-details.component.spec.ts create mode 100644 web/src/app/diagram-details/diagram-details.component.ts create mode 100644 web/src/app/investigation/investigation-main/investigation-main.component.html create mode 100644 web/src/app/investigation/investigation-main/investigation-main.component.scss create mode 100644 web/src/app/investigation/investigation-main/investigation-main.component.spec.ts create mode 100644 web/src/app/investigation/investigation-main/investigation-main.component.ts create mode 100644 web/src/app/layout-login/layout-login.component.html create mode 100644 web/src/app/layout-login/layout-login.component.scss create mode 100644 web/src/app/layout-login/layout-login.component.spec.ts create mode 100644 web/src/app/layout-login/layout-login.component.ts create mode 100644 web/src/app/layout/layout.component.html create mode 100644 web/src/app/layout/layout.component.scss create mode 100644 web/src/app/layout/layout.component.spec.ts create mode 100644 web/src/app/layout/layout.component.ts create mode 100644 web/src/app/login/login.component.html create mode 100644 web/src/app/login/login.component.scss create mode 100644 web/src/app/login/login.component.spec.ts create mode 100644 web/src/app/login/login.component.ts create mode 100644 web/src/app/main-diagram/main-diagram.component.html create mode 100644 web/src/app/main-diagram/main-diagram.component.scss create mode 100644 web/src/app/main-diagram/main-diagram.component.spec.ts create mode 100644 web/src/app/main-diagram/main-diagram.component.ts create mode 100644 web/src/app/not-found/not-found.component.html create mode 100644 web/src/app/not-found/not-found.component.scss create mode 100644 web/src/app/not-found/not-found.component.spec.ts create mode 100644 web/src/app/not-found/not-found.component.ts create mode 100644 web/src/app/password-restore/password-restore.component.html create mode 100644 web/src/app/password-restore/password-restore.component.scss create mode 100644 web/src/app/password-restore/password-restore.component.spec.ts create mode 100644 web/src/app/password-restore/password-restore.component.ts create mode 100644 web/src/app/register/register.component.html create mode 100644 web/src/app/register/register.component.scss create mode 100644 web/src/app/register/register.component.spec.ts create mode 100644 web/src/app/register/register.component.ts create mode 100644 web/src/app/services/auth-service.ts create mode 100644 web/src/app/services/authGuards/auth-guard-admin.ts create mode 100644 web/src/app/services/authGuards/auth-guard-general.ts create mode 100644 web/src/app/services/config-service.ts create mode 100644 web/src/app/services/dashboard-service.ts create mode 100644 web/src/app/services/login.service.ts create mode 100644 web/src/app/services/storage-service.ts create mode 100644 web/src/app/tokens/index.ts create mode 100644 web/src/app/user-management/user-management.component.html create mode 100644 web/src/app/user-management/user-management.component.scss create mode 100644 web/src/app/user-management/user-management.component.spec.ts create mode 100644 web/src/app/user-management/user-management.component.ts create mode 100644 web/src/assets/config.json create mode 100644 web/src/environments/environment.prod.ts create mode 100644 web/src/environments/environment.ts create mode 100644 web/src/models/httpReponseContext.ts create mode 100644 web/src/models/userTokenModel.ts create mode 100644 web/src/tsconfig.app.json create mode 100644 web/src/tsconfig.spec.json diff --git a/_old/Dockerfile b/_old/Dockerfile index 4a3bb26..ad5fc75 100644 --- a/_old/Dockerfile +++ b/_old/Dockerfile @@ -28,13 +28,13 @@ RUN apt-get update && apt-get install -y \ python3-dev # # Install Python dependencies -RUN pip install --upgrade pip setuptools wheel -RUN pip install --no-deps \ +RUN pip install --trusted-host pypi.org --upgrade pip setuptools wheel +RUN pip install --trusted-host pypi.org --no-deps \ spinetoolbox==0.7.4 \ spine_engine==0.23.4 \ spine_items==0.21.5 \ spinedb_api==0.30.5 -RUN pip install -r requirements.txt +RUN pip install --trusted-host pypi.org -r requirements.txt # # Install Julia, SpineInterface and SpineOpt RUN python -m jill install --confirm diff --git a/_old/install/install.sh b/_old/install/install.sh index f1c6b22..23a412a 100644 --- a/_old/install/install.sh +++ b/_old/install/install.sh @@ -2,8 +2,8 @@ # Copyright 2023, Battelle Energy Alliance, LLC apt update apt install python3 python3-pip git -y -python3 -m pip install pip --upgrade -python3 -m pip install -r requirements.txt +python3 -m pip install --trusted-host pip --upgrade +python3 -m pip install --trusted-host -r requirements.txt python3 -m jill install --confirm # should lock this to a commit hash julia -e 'using Pkg; Pkg.rm("SpineInterface")' || true diff --git a/api/Dockerfile b/api/Dockerfile index 3cb8108..90619fa 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -7,8 +7,8 @@ COPY . /var/www # Install dependencies RUN apt-get update \ && apt-get install -y build-essential cargo -RUN pip install --upgrade pip \ - && pip install -r requirements-test.txt +RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org--upgrade pip \ + && pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -r requirements-test.txt COPY ./etc/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod a+x /usr/local/bin/entrypoint.sh diff --git a/web/Dockerfile b/web/Dockerfile index 45b2e5f..f7995d5 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -3,7 +3,7 @@ FROM node:20-alpine WORKDIR /var/www COPY . /var/www/ - +RUN npm config set strict-ssl false RUN npm install -g @angular/cli RUN npm install diff --git a/web/angular.json b/web/angular.json index 64fe1d0..2d1198a 100644 --- a/web/angular.json +++ b/web/angular.json @@ -32,14 +32,12 @@ } ], "styles": [ - "src/styles.scss" + "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", "src/styles.scss" ], "scripts": [], "server": "src/main.server.ts", - "prerender": true, - "ssr": { - "entry": "server.ts" - } + "prerender": false, + "ssr": false }, "configurations": { "production": { diff --git a/web/package-lock.json b/web/package-lock.json index 14cf8ea..6af1399 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,15 +9,19 @@ "version": "0.0.0", "dependencies": { "@angular/animations": "^18.2.0", + "@angular/cdk": "^17.0.0", "@angular/common": "^18.2.0", "@angular/compiler": "^18.2.0", "@angular/core": "^18.2.0", "@angular/forms": "^18.2.0", + "@angular/material": "^17.0.0", "@angular/platform-browser": "^18.2.0", "@angular/platform-browser-dynamic": "^18.2.0", "@angular/platform-server": "^18.2.0", "@angular/router": "^18.2.0", "@angular/ssr": "^18.2.8", + "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", "express": "^4.18.2", "rxjs": "~7.8.0", "tslib": "^2.3.0", @@ -356,6 +360,23 @@ } } }, + "node_modules/@angular/cdk": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.0.0.tgz", + "integrity": "sha512-8F1z0YhDjKWqB29+AUh33OKr2Ts5PVUUogVXEVmmPipPDtj82iIQLbtBnR6xpWvmsw9mS3oD76EwBoUhHQgi6g==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "18.2.8", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.8.tgz", @@ -455,6 +476,36 @@ "typescript": ">=5.4 <5.6" } }, + "node_modules/@angular/compiler-cli/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/compiler-cli/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@angular/core": { "version": "18.2.8", "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.8.tgz", @@ -489,6 +540,71 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.0.0.tgz", + "integrity": "sha512-rd7H7NhkDPDiyLHADm2FHOJlmgaWV7ZYNYPe/4yTXlt++GTSLhKus+PTCZYVsKGlA3mxDhNnC1RY+fdjtx/G2A==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/auto-init": "15.0.0-canary.a246a4439.0", + "@material/banner": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/button": "15.0.0-canary.a246a4439.0", + "@material/card": "15.0.0-canary.a246a4439.0", + "@material/checkbox": "15.0.0-canary.a246a4439.0", + "@material/chips": "15.0.0-canary.a246a4439.0", + "@material/circular-progress": "15.0.0-canary.a246a4439.0", + "@material/data-table": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dialog": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/drawer": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/fab": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/floating-label": "15.0.0-canary.a246a4439.0", + "@material/form-field": "15.0.0-canary.a246a4439.0", + "@material/icon-button": "15.0.0-canary.a246a4439.0", + "@material/image-list": "15.0.0-canary.a246a4439.0", + "@material/layout-grid": "15.0.0-canary.a246a4439.0", + "@material/line-ripple": "15.0.0-canary.a246a4439.0", + "@material/linear-progress": "15.0.0-canary.a246a4439.0", + "@material/list": "15.0.0-canary.a246a4439.0", + "@material/menu": "15.0.0-canary.a246a4439.0", + "@material/menu-surface": "15.0.0-canary.a246a4439.0", + "@material/notched-outline": "15.0.0-canary.a246a4439.0", + "@material/radio": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/segmented-button": "15.0.0-canary.a246a4439.0", + "@material/select": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/slider": "15.0.0-canary.a246a4439.0", + "@material/snackbar": "15.0.0-canary.a246a4439.0", + "@material/switch": "15.0.0-canary.a246a4439.0", + "@material/tab": "15.0.0-canary.a246a4439.0", + "@material/tab-bar": "15.0.0-canary.a246a4439.0", + "@material/tab-indicator": "15.0.0-canary.a246a4439.0", + "@material/tab-scroller": "15.0.0-canary.a246a4439.0", + "@material/textfield": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tooltip": "15.0.0-canary.a246a4439.0", + "@material/top-app-bar": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^17.0.0 || ^18.0.0", + "@angular/cdk": "17.0.0", + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", + "@angular/platform-browser": "^17.0.0 || ^18.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "18.2.8", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.8.tgz", @@ -3541,6 +3657,808 @@ "win32" ] }, + "node_modules/@material/animation": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-0eV06UGYeuFwC/4t+yjg3LCRGRLq72ybBtJYzcBDpP4ASTjie0WmpAOFJYXRq2U5X/yxLviDMhpRemoSUjgZ0Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/auto-init": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-0QfmjT5elQ10hCxToVgq/WaC3301tVH1sJaO3O2yocVzr7s6iWm8/zch16V5hcHzQHbtcT3Rf4y1ZzmdNys2Iw==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/banner": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-PBLgH7JEbEpTkLy33oyWXUhIFmSsdOrR6Gn6qIgQRo1qrnk5RSBGW2gEq4Z6793vjxM107gKudDb23E4Fcu4vg==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/button": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/base": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-/ob3v3IFU8q2gGdVNWw5kNPjW2mRTeBIz1YdhGWUmRxKn2Kl8bdLOvrAmZtQMmPn/4cGXvinxpec/zVBWQKDkA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/button": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-rGpVRde0Aqhv2t9QvT8Zl3HvG89BeUNPOpgfpaLBZ4SGGAO4rIrckl/eCENibKgmmdCKcYZlG9gc5abQVPfUvw==", + "license": "MIT", + "dependencies": { + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/card": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-+rYUnBPgv5QVF6BeUs3toIRdSwFVohGmjk2ptTXMZkKxqAJt7Nr9Znbm3Ym2hD8GUHJeh3pyGFvEs6rG6JMYAw==", + "license": "MIT", + "dependencies": { + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/checkbox": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-sQwHzm1TSxHUoPrqplWTk/BhyzdDhzcwlbucwJK9W0o9WXMDk+d9PvcCxpP/9sAnVqZk42BfE89Y0T1DHglZ9A==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/chips": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-TiV9WJ5taEHPGWPhXbxJvUJhLzThg+VpK7aAlvL4RurtmJ7pURuEdRS4Z6o0OEqi3wKQ4z/+K44kZUn/+9HALg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/checkbox": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/circular-progress": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-+QTfyExPWzgm2tqMInd32qQOftsC1b8MUhAhZSfuecYBfqAc7KZkQEKa2nm4y8EHKMFWe8/DcxLV6IxMBLgHwA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/progress-indicator": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/data-table": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-89qVOjR7gqby6fsmh7tKj29SjQ2sGLXu2IzCeX3Vni4mz+xxo5dv11jxYNADvdgJDfhyDJFPh1FlqAH7O09nFA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/checkbox": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/icon-button": "15.0.0-canary.a246a4439.0", + "@material/linear-progress": "15.0.0-canary.a246a4439.0", + "@material/list": "15.0.0-canary.a246a4439.0", + "@material/menu": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/select": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/density": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-h8BJVCWkPR97WeWCN6/atVbSOP8J4+ZbbssidcwsnX7b3+3IaWdtBxGii25dsILX8pUVwwqxVis24y211b+8rg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/dialog": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-4lyxd+5ccOEMUGKzZcssaYyzkCsYTpYCSQSANR0toQPLv3voDwKMfA709uZI6+nL7Re6Xdf7jx8qe+QpTTjVcw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/button": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/icon-button": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/dom": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-AftSOGQoQg/Ys2kOVjZzvqWmsnhg3Kam/2UC4Gj0DMMCu36J4MAoD+3PpnOd1aG3wiJKtUXR2vPIwE8I/PM9yg==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/drawer": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-/JUmbzRBaikdbZ250yA9ZTPqp2W5nGvvuHYoNVAAmtOmxuwGvvNNpWiVZy2lIYeYcf1hA7hJ5mEQxs0aSD7iWQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/list": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/elevation": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-lwPIOb8fHyOljIWYcVLPT73dPIEOKat/CXu6gqYIVMQgZQIksQNUA7z1O3l7apkRSuYUOYSXqrgU7AnWP4KcJg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/fab": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-XUex3FNqxPD1i/4jITucB/RWTNkkdv52mbNmwrvbuThZlhuhyH9GzOQYTDop/b2783TPcv++xr8UUbuh8GWYzA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/feature-targeting": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-/SU9X5y8CRp6RS9qnjnM/N5qfsJ8bYILpR841eZmN6DLqMupaM9Yy7Mx8+v/QvpBLLhk+jmu79nFzwkwW54d6Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/floating-label": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-832qZ/qxKx0KUatoeVY3Q2NmboVgiWBG0/1VsbJyodHrgQWfnBOHgLE+M322o6uM3OhvO+kWm4iYbvwhmLZGsw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/focus-ring": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-ar0BtACFS3K14k/enAg0ePeEA/f/RJY4Ji4L/00Dw/B3XVpNRbqLH49jkcbtcQjdTS0FEyk2sWSNMZl6wVi0/A==", + "license": "MIT", + "dependencies": { + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0" + } + }, + "node_modules/@material/form-field": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-Q/+ErgtAUFUPPUmWA1m5IP5voiN8XjPRwyoAlFxSTa/4t+EA5B18Z8Bsn9b6I0AC8RHke06H7UWrKz8XUDIFpw==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/icon-button": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-Igyo94rkIlqC91BR1Tv+WLTz1ZWcZZjl1xU7Vsx8mbWA1PnaRDUTNVV5LFi4e0ORp6GSblFTImpHngEy4agMEg==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/image-list": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-Rcj3q7Tp7Nwbe5ht6ptTc3zqK8TSDJHaPDBf+kzi0kkh6MAB4qoHPgn+HnA+zIZ79CScU56bN7zjA6XYaZvsLw==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/layout-grid": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-bkfxZuVzgtjEJgR3n8pvDQbe88ffULDJ5d2DF34IR8SOiRmQcj7UzqAt95XwIUcWlfisLCoIryP4U8XSpFb1EQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/line-ripple": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-20WmwRrejmtOdI37+959UqEVIjbMtAXlkDOkfCIA3OUhp+oZSjVkCqKxI16jxxVlnzJ353fy8xeSKzOHe4sExQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/linear-progress": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-IcCd4476pXHloTYadHDJ+2c2lntoVigeNnQEiD/ASQTKqKrJqkIdvvczFm9Ryu+V2+TKhp7vvQGFLUMaLPcmhw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/progress-indicator": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/list": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-4H5dKIjCUGIPmKjfcegV0SBybD5NNdHp26OU6sovvWIvxSGQtDJr6z9I7i+0vF/HIS5ScbHD2+9/txtL80iqCA==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/menu": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-2HOHQAIdWQtXjSvEIrW3lnbcIwFf5XaQhFzCEZ04FcSGApc4iLwsmRFVW3PzWx+mVrUrEfO/K42DVULIX9J1Pg==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/list": "15.0.0-canary.a246a4439.0", + "@material/menu-surface": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/menu-surface": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-4h4wZ0Rs7qBg1Otldw8ljp+LCULNL42pqbqcTXhKAkJM7pHcSw4k7IfoThSRLU3+V8T3/+qiAXyeQix2OGHzwg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/notched-outline": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-zmRZHJ+5cOWsBatRyK50wuht78olXySyKOJIIEmy8lxSMZefI1764u0mr8tS1KYF8vSAl5cUlwCC3/2Njz1FPg==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/floating-label": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/progress-indicator": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-92HM5niUnqG5Y3M/xkscBD+2lkaWPDcIRPo0RHPYcyldL+EhWRv/sdQpfdiXw/h3uvKSowKxBMCHm8krAyf+sQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/radio": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-on8EVztWXc/ajcaowFZ31ClGADYxQrhj4ulMne0NxdHHWQ44ttf5aXOVqtv5mxeOzrRACOkQyTUXBG07yTWCEQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/ripple": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-Vl615/PIBpBD+IOI9Xypz0SV3RsmYJYSNx890Rih7irhUOaPsOUBmTYOWF5AsGBynqLcXoTNVhK92drYLKtJwQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/rtl": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-pgJFw8ZRpWGpwv7ZuBTJ+WdNmFBKoLVoMbbxKQWTHXVwhAqn3aoIq95o62T5QeEG/+sguNShdquG45CpAMmSRw==", + "license": "MIT", + "dependencies": { + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/segmented-button": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-oqGHs2C7C+yJW/xZf/wP8jBGLs6HcerhM3CsorLAEMH3MGuIlVC17WcisBewEWucsILYEWbySXy/7T4h6/psZA==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/touch-target": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/select": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-odoNLiVOgdwbEeePkjHtlr43pjskDwyO8hi4z3jcud1Rg1czk5zoJ2mUI0+olOJjBQ26PGocwrSLqf3qaThbIA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/floating-label": "15.0.0-canary.a246a4439.0", + "@material/line-ripple": "15.0.0-canary.a246a4439.0", + "@material/list": "15.0.0-canary.a246a4439.0", + "@material/menu": "15.0.0-canary.a246a4439.0", + "@material/menu-surface": "15.0.0-canary.a246a4439.0", + "@material/notched-outline": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/shape": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-rcWPlCoHyP79ozeEKk73KWt9WTWdh6R68+n75l08TSTvnWZB5RRTmsI9BMkz55O9OJD/8H8ZsOxBe4x2QXUT7w==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/slider": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-is1BSBpxaXBBv+wSVpe9WGWmWl59yJEeDNubTES2UFD0er3BmA+PdKkL09vvytDnBcbKf77TbxaRiUSGVaKUQA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/snackbar": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-2NAtC1qozR/uajszZnPy08Ej8HNnpgvCjNCBerDN4SLH2Q0/aWrVrUjqRCp2ayAvsX+szoroGbCboMhaWRzDuQ==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/button": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/icon-button": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/switch": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-o0wcbYgm2yRs4een5uxT4RJnJ003DxXe33rk8vTBG2o7cdiSR3X7GJQxeIK3D9wPgWCAwBLhNYSzXrlTL5pkMw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-HGLK774uMeLnhbjDJBOjft7S6SurZnKb+6Und88OMDUVUEG6MkFBAKQQr09iBIeLE2sUAiGQhBVQtb7LJKwolQ==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/focus-ring": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/tab-indicator": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-bar": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-dMQb1vXsBchQXcjbwgJZIGqTZHngm+3QGSOSb4LWjqHIgC5+w2RRrHsIAjNTyRhKssJ9nKKrbpM/Yz5vTPWH6w==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/tab": "15.0.0-canary.a246a4439.0", + "@material/tab-indicator": "15.0.0-canary.a246a4439.0", + "@material/tab-scroller": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-indicator": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-gG2BgHT+ggKnUOaT8LjmH/+9nknRLh8v9qemrhUkDuCtZ8inlaC33OVbbxfrpQW3J+UzBh5YCUSC+2KrN39uUA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-scroller": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-6KvBpalc4SwLbHFm0rnuIE64VffUj7AKhnPc+mqM6VmxOvDzQ/ZSYga0rWlUfM4mCDFX3ZkSxim+iNzVF+Ejaw==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/tab": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/textfield": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-4BW5bUERPlIeiPnLSby21h1/xDmySuAG9Ucn1LM801a0+5mK3IwWb8031AP3filKZZqTx5JJvOJYZd6/OWBJVA==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/density": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/floating-label": "15.0.0-canary.a246a4439.0", + "@material/line-ripple": "15.0.0-canary.a246a4439.0", + "@material/notched-outline": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/theme": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-HWxC5Nhz8JZKTLTVmAsNxIGB3Kzr53+YFMg327S8/XuEDmI0RFHFvtwM9rADmyrHFBmUaVhV4iELyxFdi67c9w==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tokens": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-+5iGfQ51YSb0Qau8uC6/jHXCSC3enKaQKDf/iPHfuXAe04UznW3tmm1/Ju227aZXNISTJcnQYa2rpm1M14MeUg==", + "license": "MIT", + "dependencies": { + "@material/elevation": "15.0.0-canary.a246a4439.0" + } + }, + "node_modules/@material/tooltip": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-Ja2Z4aZQkYWD6InXA+MG4M9zdKR6dYsXXlYzQppYpfcQzXylZqh5Y7WBLulG5fA2o83pHVwILfwFZM7j7ht08Q==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/button": "15.0.0-canary.a246a4439.0", + "@material/dom": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/tokens": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/top-app-bar": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-twQchmCa1In/FFrALPYojgeM8vmV7KH96wRY9NmPSJ046ANgPCicLBgLuSzrLETCFqAwbztqzxSG4xMBL81rYg==", + "license": "MIT", + "dependencies": { + "@material/animation": "15.0.0-canary.a246a4439.0", + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/elevation": "15.0.0-canary.a246a4439.0", + "@material/ripple": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/shape": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "@material/typography": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/touch-target": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-ubyD1TUjZnRPEdDnk6Lrcm2ZsjnU7CV5y7IX8pj9IPawiM6bx4FkjZBxUvclbv3WiTGk5UOnwPOySYAJYAMQ1w==", + "license": "MIT", + "dependencies": { + "@material/base": "15.0.0-canary.a246a4439.0", + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/rtl": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/typography": { + "version": "15.0.0-canary.a246a4439.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.a246a4439.0.tgz", + "integrity": "sha512-eXzBl9ROzWZ+41nan5pCrn1C/Zq3o/VsrLFaGv8fdRmhRR6/wHMeuvCCwGf5VtEmWdAE9FpJzRU/4ZPiJCJUyg==", + "license": "MIT", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.a246a4439.0", + "@material/theme": "15.0.0-canary.a246a4439.0", + "tslib": "^2.1.0" + } + }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", @@ -3968,6 +4886,17 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", @@ -5280,16 +6209,51 @@ "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" + "node_modules/bootstrap-icons": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", + "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -5556,19 +6520,41 @@ "license": "MIT" }, "node_modules/chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 8.10.0" }, "funding": { "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/chownr": { @@ -8741,31 +9727,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/karma/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/karma/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -8805,19 +9766,6 @@ "dev": true, "license": "MIT" }, - "node_modules/karma/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/karma/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -8828,32 +9776,6 @@ "node": ">=8" } }, - "node_modules/karma/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/karma/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/karma/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10854,7 +11776,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "entities": "^4.5.0" @@ -11348,17 +12270,29 @@ } }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">= 14.16.0" + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/reflect-metadata": { @@ -11730,6 +12664,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/safevalues": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/safevalues/-/safevalues-0.3.4.tgz", + "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==", + "license": "Apache-2.0" + }, "node_modules/sass": { "version": "1.77.6", "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", @@ -11789,70 +12729,6 @@ } } }, - "node_modules/sass/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/sass/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/sass/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/sass/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -14022,31 +14898,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/webpack-dev-server/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -14068,19 +14919,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webpack-dev-server/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", @@ -14132,32 +14970,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webpack-dev-server/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/webpack-dev-server/node_modules/rimraf": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", diff --git a/web/package.json b/web/package.json index aa95ae0..bbc1a71 100644 --- a/web/package.json +++ b/web/package.json @@ -12,15 +12,19 @@ "private": true, "dependencies": { "@angular/animations": "^18.2.0", + "@angular/cdk": "^17.0.0", "@angular/common": "^18.2.0", "@angular/compiler": "^18.2.0", "@angular/core": "^18.2.0", "@angular/forms": "^18.2.0", + "@angular/material": "^17.0.0", "@angular/platform-browser": "^18.2.0", "@angular/platform-browser-dynamic": "^18.2.0", "@angular/platform-server": "^18.2.0", "@angular/router": "^18.2.0", "@angular/ssr": "^18.2.8", + "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", "express": "^4.18.2", "rxjs": "~7.8.0", "tslib": "^2.3.0", diff --git a/web/src/app/about/about.component.html b/web/src/app/about/about.component.html new file mode 100644 index 0000000..6094aa9 --- /dev/null +++ b/web/src/app/about/about.component.html @@ -0,0 +1 @@ +

about works!

diff --git a/web/src/app/about/about.component.scss b/web/src/app/about/about.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/about/about.component.spec.ts b/web/src/app/about/about.component.spec.ts new file mode 100644 index 0000000..74d6d9e --- /dev/null +++ b/web/src/app/about/about.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AboutComponent } from './about.component'; + +describe('AboutComponent', () => { + let component: AboutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AboutComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AboutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/about/about.component.ts b/web/src/app/about/about.component.ts new file mode 100644 index 0000000..6f52bf4 --- /dev/null +++ b/web/src/app/about/about.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-about', + standalone: true, + imports: [], + templateUrl: './about.component.html', + styleUrl: './about.component.scss' +}) +export class AboutComponent { + +} diff --git a/web/src/app/app.component.html b/web/src/app/app.component.html index 36093e1..32f0d59 100644 --- a/web/src/app/app.component.html +++ b/web/src/app/app.component.html @@ -7,320 +7,10 @@ - -
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
+ +
diff --git a/web/src/app/app.component.scss b/web/src/app/app.component.scss index e69de29..661a6ad 100644 --- a/web/src/app/app.component.scss +++ b/web/src/app/app.component.scss @@ -0,0 +1,8 @@ +.main{ + font-family: "Roboto", sans-serif; + margin:0 +} + +.content{ + margin:0 +} \ No newline at end of file diff --git a/web/src/app/app.component.ts b/web/src/app/app.component.ts index a063aa1..c174922 100644 --- a/web/src/app/app.component.ts +++ b/web/src/app/app.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { LayoutComponent } from './layout/layout.component'; + @Component({ selector: 'app-root', standalone: true, diff --git a/web/src/app/app.config.server.ts b/web/src/app/app.config.server.ts index b4d57c9..c08aab3 100644 --- a/web/src/app/app.config.server.ts +++ b/web/src/app/app.config.server.ts @@ -1,10 +1,19 @@ import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; import { appConfig } from './app.config'; +import { LOCAL_STORAGE } from './tokens'; const serverConfig: ApplicationConfig = { providers: [ - provideServerRendering() + provideServerRendering(), + { + provide: LOCAL_STORAGE, + useFactory: () => ({ + getItem: () => {}, + setItem: () => {}, + removeItem: () => {}, + }), + }, ] }; diff --git a/web/src/app/app.config.ts b/web/src/app/app.config.ts index 52cd710..3ee0985 100644 --- a/web/src/app/app.config.ts +++ b/web/src/app/app.config.ts @@ -1,9 +1,38 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { ApplicationConfig, provideZoneChangeDetection, PLATFORM_ID } from '@angular/core'; import { provideRouter } from '@angular/router'; - +import { isPlatformServer } from '@angular/common'; +import { LOCAL_STORAGE } from './tokens'; import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { ConfigService } from './services/config-service'; +import { DashService } from './services/dashboard-service'; +import { AuthService } from './services/auth-service'; +import { provideHttpClient, withFetch } from '@angular/common/http'; +import {ReactiveFormsModule} from '@angular/forms'; +import { LoginService } from './services/login.service'; export const appConfig: ApplicationConfig = { - providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration()] + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + provideClientHydration(), + provideAnimations(), + ConfigService, + AuthService, + DashService, + LoginService, + provideHttpClient(withFetch()), + ReactiveFormsModule, + { + provide: LOCAL_STORAGE, + useFactory: (platformId: object) => { + if (isPlatformServer(platformId)) { + return {}; // Return an empty object on the server + } + return localStorage; // Use the browser's localStorage + }, + deps: [PLATFORM_ID], + }, + ] }; diff --git a/web/src/app/app.routes.ts b/web/src/app/app.routes.ts index dc39edb..a27d06e 100644 --- a/web/src/app/app.routes.ts +++ b/web/src/app/app.routes.ts @@ -1,3 +1,71 @@ import { Routes } from '@angular/router'; +import { LayoutComponent } from './layout/layout.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; +import { LayoutLoginComponent } from './layout-login/layout-login.component'; +import { AboutComponent } from './about/about.component'; +import { NotFoundComponent } from './not-found/not-found.component'; +import { UserManagementComponent } from './user-management/user-management.component'; +import { LoginComponent } from './login/login.component'; +import { RegisterComponent } from './register/register.component'; +import { PasswordRestoreComponent } from './password-restore/password-restore.component'; +import { AuthGuardGeneral } from './services/authGuards/auth-guard-general'; +import { InvestigationMainComponent } from './investigation/investigation-main/investigation-main.component'; -export const routes: Routes = []; +export const routes: Routes = [ + + + { path: '', redirectTo: 'login/signin', pathMatch: 'full' }, //default route + { + path: 'login', + component: LayoutLoginComponent, + children: [ + { + path: "signin", + component: LoginComponent + }, + { + path: "register", + component: RegisterComponent + }, + { + path: "passowrdReset", + component: PasswordRestoreComponent, + }, + { path: '**', component: NotFoundComponent } + ] + }, + + { + path: 'main', + component: LayoutComponent, + children: [ + { + path: "", + redirectTo: 'dashboard' , + pathMatch: 'full' + }, + { + path: "dashboard", + component: DashboardComponent, + canActivate: [AuthGuardGeneral] + }, + { + path: "investigation/:id", + component: InvestigationMainComponent, + canActivate: [AuthGuardGeneral] + }, + { + path: "about", + component: AboutComponent, + canActivate: [AuthGuardGeneral] + }, + { + path: "user", + component: UserManagementComponent, + canActivate: [AuthGuardGeneral], + }, + { path: '**', component: NotFoundComponent } + ] + }, + +]; diff --git a/web/src/app/dashboard/dashboard.component.html b/web/src/app/dashboard/dashboard.component.html new file mode 100644 index 0000000..d765e8c --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.html @@ -0,0 +1,59 @@ + +
+ +
+
+
Recent Items
+
+ +
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+ +
\ No newline at end of file diff --git a/web/src/app/dashboard/dashboard.component.scss b/web/src/app/dashboard/dashboard.component.scss new file mode 100644 index 0000000..0d0b7a4 --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.scss @@ -0,0 +1,15 @@ +.dash-container{ + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 1em; + height: calc(100% - 1em); + max-width: 1180px; + margin: auto; +} +.dash-header{ + display: flex; + flex-direction: column; + + border-bottom: solid 1px var(--black); +} \ No newline at end of file diff --git a/web/src/app/dashboard/dashboard.component.spec.ts b/web/src/app/dashboard/dashboard.component.spec.ts new file mode 100644 index 0000000..30e39a2 --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DashboardComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/dashboard/dashboard.component.ts b/web/src/app/dashboard/dashboard.component.ts new file mode 100644 index 0000000..93a1979 --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.ts @@ -0,0 +1,31 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../services/config-service'; +import { DashService } from '../services/dashboard-service'; +import { HostBinding } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { SafeStyle } from '@angular/platform-browser'; +import { RecentItemsComponent } from './recent-items/recent-items.component'; + +@Component({ + selector: 'app-dashboard', + standalone: true, + imports: [RecentItemsComponent], + templateUrl: './dashboard.component.html', + styleUrl: './dashboard.component.scss', + // schemas: [NO_ERRORS_SCHEMA] +}) +export class DashboardComponent { + constructor( + public configSvc: ConfigService, + public dashSvc: DashService, + public sanitizer: DomSanitizer, + ){ + } + ngOnint(){ + console.log(this.dashSvc.loadDashBoard()) + } + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('display: flex; flex-grow: 1'); + } +} diff --git a/web/src/app/dashboard/recent-items/recent-items.component.html b/web/src/app/dashboard/recent-items/recent-items.component.html new file mode 100644 index 0000000..01b9073 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.html @@ -0,0 +1,18 @@ +
+ + + + + + + + + + + + + + + +
Title {{element.title}} Date {{element.date | date}}
+
\ No newline at end of file diff --git a/web/src/app/dashboard/recent-items/recent-items.component.scss b/web/src/app/dashboard/recent-items/recent-items.component.scss new file mode 100644 index 0000000..6e79032 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.scss @@ -0,0 +1,8 @@ +.recent-item-table{ + overflow-y: scroll; +} +.recent-items-container{ + overflow: auto; + display: flex; + flex-grow: 1; +} \ No newline at end of file diff --git a/web/src/app/dashboard/recent-items/recent-items.component.spec.ts b/web/src/app/dashboard/recent-items/recent-items.component.spec.ts new file mode 100644 index 0000000..a4e8c32 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RecentItemsComponent } from './recent-items.component'; + +describe('RecentItemsComponent', () => { + let component: RecentItemsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RecentItemsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RecentItemsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/dashboard/recent-items/recent-items.component.ts b/web/src/app/dashboard/recent-items/recent-items.component.ts new file mode 100644 index 0000000..256b097 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.ts @@ -0,0 +1,68 @@ +import { Component } from '@angular/core'; +import {MatTableModule} from '@angular/material/table'; +import { ConfigService } from '../../services/config-service'; +import { HttpClient } from '@angular/common/http'; +import {DatePipe} from '@angular/common'; +import { HostBinding } from '@angular/core'; +import { SafeStyle } from '@angular/platform-browser'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-recent-items', + standalone: true, + imports: [MatTableModule,DatePipe], + templateUrl: './recent-items.component.html', + styleUrl: './recent-items.component.scss' +}) +export class RecentItemsComponent { + + constructor( + private configSvc: ConfigService, + private http: HttpClient, + public sanitizer: DomSanitizer, + private router: Router, + ){ + this.getRecentItems(); + } + + displayedColumns: string[] = ['title', 'date']; + dataSource: Array = []; + + getRecentItems(){ + if(this.configSvc.offline){ + //return default item + var timeSpan = 0 + var retVal = []; + var now =new Date(); + for(var i = 0; i < 8; i++){ + var rand = Math.floor(Math.random() * 20000); + timeSpan += rand; + retVal.push({"id": i, "title": "Title " + i, "date": new Date(now.getTime() - timeSpan)}) + } + this.dataSource = retVal; + + + } else { + this.http.get(this.configSvc.apiUrl + 'user/getRecentItems').subscribe({ + next: (v) => {}, + error: (e) => {}, + complete: () => {}, + }) + } + + } + + select(id: number ){ + this.router.navigate(['main/investigation/' + id]) + } + + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('display: flex; flex-grow: 1, flex-direction: column'); + } + + + + +} diff --git a/web/src/app/diagram-details/diagram-details.component.html b/web/src/app/diagram-details/diagram-details.component.html new file mode 100644 index 0000000..b37c192 --- /dev/null +++ b/web/src/app/diagram-details/diagram-details.component.html @@ -0,0 +1 @@ +

diagram-details works!

diff --git a/web/src/app/diagram-details/diagram-details.component.scss b/web/src/app/diagram-details/diagram-details.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/diagram-details/diagram-details.component.spec.ts b/web/src/app/diagram-details/diagram-details.component.spec.ts new file mode 100644 index 0000000..d171ffc --- /dev/null +++ b/web/src/app/diagram-details/diagram-details.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DiagramDetailsComponent } from './diagram-details.component'; + +describe('DiagramDetailsComponent', () => { + let component: DiagramDetailsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DiagramDetailsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DiagramDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/diagram-details/diagram-details.component.ts b/web/src/app/diagram-details/diagram-details.component.ts new file mode 100644 index 0000000..8337258 --- /dev/null +++ b/web/src/app/diagram-details/diagram-details.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-diagram-details', + standalone: true, + imports: [], + templateUrl: './diagram-details.component.html', + styleUrl: './diagram-details.component.scss' +}) +export class DiagramDetailsComponent { + +} diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.html b/web/src/app/investigation/investigation-main/investigation-main.component.html new file mode 100644 index 0000000..fffdab5 --- /dev/null +++ b/web/src/app/investigation/investigation-main/investigation-main.component.html @@ -0,0 +1 @@ +

investigation-main works!

diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.scss b/web/src/app/investigation/investigation-main/investigation-main.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.spec.ts b/web/src/app/investigation/investigation-main/investigation-main.component.spec.ts new file mode 100644 index 0000000..0531c9e --- /dev/null +++ b/web/src/app/investigation/investigation-main/investigation-main.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InvestigationMainComponent } from './investigation-main.component'; + +describe('InvestigationMainComponent', () => { + let component: InvestigationMainComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [InvestigationMainComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(InvestigationMainComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.ts b/web/src/app/investigation/investigation-main/investigation-main.component.ts new file mode 100644 index 0000000..62a48e4 --- /dev/null +++ b/web/src/app/investigation/investigation-main/investigation-main.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-investigation-main', + standalone: true, + imports: [], + templateUrl: './investigation-main.component.html', + styleUrl: './investigation-main.component.scss' +}) +export class InvestigationMainComponent { + +} diff --git a/web/src/app/layout-login/layout-login.component.html b/web/src/app/layout-login/layout-login.component.html new file mode 100644 index 0000000..7bb0591 --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/web/src/app/layout-login/layout-login.component.scss b/web/src/app/layout-login/layout-login.component.scss new file mode 100644 index 0000000..2e9dac6 --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.scss @@ -0,0 +1,80 @@ +.layout-login-container{ + height: 100%; + font-family: "Roboto", sans-serif; + margin: 0; + display: flex; + flex-direction: column; + flex-grow: 1; + width: 100%; + overflow-y: hidden; +} + +.layout-login-header{ + padding: 12px; + width: calc(100% - 24px); + font-size: 1.75em; + font-weight: 700; + display: flex; + flex-direction: row; + cursor: pointer; + +} + +.layout-login-body{ + display: flex; + flex-direction: column; + flex-grow: 1; + height: 100%; + +} + +.layout-login-footer{ + display: flex; + height: 3em; + flex-direction: row; + background-color: var(--secondary-color); +} + +// Header +.header-title{ + margin-right: -6px; +} + +.header-right-items{ + display:flex; + margin-left: auto; + // border-left: 1px solid white; + align-items: center; +} + +.header-icons{ + font-size: 26px; +} +.header-icons:hover{ + color:white; + cursor: pointer; +} +.header-settings-icon{ + width: 24px !important; + height: 24px !important; + padding: 0px !important; + display: inline-flex !important; + align-items: center; + justify-content: center; + + & > *[role=img] { + width: 20px; + height: 20px; + font-size: 20px; + + svg { + width: 20px; + height: 20px; + } + } + + .mat-mdc-button-touch-target { + width: 24px !important; + height: 24px !important; + } +} diff --git a/web/src/app/layout-login/layout-login.component.spec.ts b/web/src/app/layout-login/layout-login.component.spec.ts new file mode 100644 index 0000000..9d27533 --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LayoutLoginComponent } from './layout-login.component'; + +describe('LayoutLoginComponent', () => { + let component: LayoutLoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LayoutLoginComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LayoutLoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/layout-login/layout-login.component.ts b/web/src/app/layout-login/layout-login.component.ts new file mode 100644 index 0000000..a34544f --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.ts @@ -0,0 +1,36 @@ +import { Component } from '@angular/core'; +import { Router, RouterOutlet } from '@angular/router'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatIconModule} from '@angular/material/icon'; +import {MatButtonModule} from '@angular/material/button'; +import { ConfigService } from '../services/config-service'; +import { HostBinding } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { SafeStyle } from '@angular/platform-browser'; + +@Component({ + selector: 'app-layout-login', + standalone: true, + imports: [RouterOutlet,MatIconModule,MatSlideToggleModule,MatMenuModule,MatButtonModule], + templateUrl: './layout-login.component.html', + styleUrl: './layout-login.component.scss' +}) +export class LayoutLoginComponent { + + constructor( + public configSvc: ConfigService, + private router: Router, + public sanitizer: DomSanitizer, + ){} + + + ngOnint(){ + console.log("Run any initial api checks here") + } + + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('height: 100%, width: 100%'); + } +} diff --git a/web/src/app/layout/layout.component.html b/web/src/app/layout/layout.component.html new file mode 100644 index 0000000..e9c5698 --- /dev/null +++ b/web/src/app/layout/layout.component.html @@ -0,0 +1,64 @@ + +
+ +
+
+ Resdeeds +

+ logo image needed +

+
+
+ +
+ {{userName}} +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ + +
+ My Footer +
+
+
+
\ No newline at end of file diff --git a/web/src/app/layout/layout.component.scss b/web/src/app/layout/layout.component.scss new file mode 100644 index 0000000..27dd915 --- /dev/null +++ b/web/src/app/layout/layout.component.scss @@ -0,0 +1,83 @@ +.layout-container{ + height: 100%; + font-family: "Roboto", sans-serif; + margin: 0; + display: flex; + flex-direction: column; + flex-grow: 1; + width: 100%; + overflow-y: hidden; +} + +.layout-header{ + padding: 12px; + width: calc(100% - 24px); + font-size: 1.75em; + font-weight: 700; + display: flex; + flex-direction: row; + cursor: pointer; + +} + +.layout-body{ + display: flex; + flex-direction: column; + flex-grow: 1; + +} + +.layout-footer{ + display: flex; + height: 3em; + flex-direction: row; + background-color: var(--secondary-color); +} + +// Header +.header-title{ + margin-right: -6px; +} + +.header-right-items{ + display:flex; + margin-left: auto; + // border-left: 1px solid white; + align-items: center; +} + +.header-icons{ + font-size: 26px; +} +.header-icons:hover{ + color:white; + cursor: pointer; +} +.header-settings-icon{ + width: 24px !important; + height: 24px !important; + padding: 0px !important; + display: inline-flex !important; + align-items: center; + justify-content: center; + + & > *[role=img] { + width: 20px; + height: 20px; + font-size: 20px; + + svg { + width: 20px; + height: 20px; + } + } + + .mat-mdc-button-touch-target { + width: 24px !important; + height: 24px !important; + } +} +.header-username-container{ + font-size: 50%; + margin-right: 2em; +} diff --git a/web/src/app/layout/layout.component.spec.ts b/web/src/app/layout/layout.component.spec.ts new file mode 100644 index 0000000..2c3d7ac --- /dev/null +++ b/web/src/app/layout/layout.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {MatIconModule} from '@angular/material/icon'; +import { LayoutComponent } from './layout.component'; + +describe('LayoutComponent', () => { + let component: LayoutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LayoutComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/layout/layout.component.ts b/web/src/app/layout/layout.component.ts new file mode 100644 index 0000000..cf9214d --- /dev/null +++ b/web/src/app/layout/layout.component.ts @@ -0,0 +1,48 @@ +import { Component } from '@angular/core'; +import { Router, RouterOutlet } from '@angular/router'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatIconModule} from '@angular/material/icon'; +import {MatButtonModule} from '@angular/material/button'; +import { userToken } from '../../models/userTokenModel'; +import { LoginService } from '../services/login.service'; + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [RouterOutlet,MatIconModule,MatSlideToggleModule,MatMenuModule,MatButtonModule], + templateUrl: './layout.component.html', + styleUrl: './layout.component.scss' +}) +export class LayoutComponent { + + userName: string = "User Not Set" + + + constructor( + private router: Router, + private loginSvc: LoginService, + ) {} + public test(){ + console.log("TEST") + } + + public getUserName(){ + var userTokenString = localStorage.getItem("userToken"); + if(userTokenString == null){ + console.log("User has navigated to the main layout without being logged in. Please check AuthGuards") + return "User Token Error"; + } + return JSON.parse(userTokenString).username; + } + public navigate(url: string){ + //do any work needed before swapping urls (checking for save, leave confirmation, etc) + console.log(url) + this.router.navigate(['/main/' + url]); + + } + public logout(){ + this.loginSvc.logout(); + } + +} diff --git a/web/src/app/login/login.component.html b/web/src/app/login/login.component.html new file mode 100644 index 0000000..78b946d --- /dev/null +++ b/web/src/app/login/login.component.html @@ -0,0 +1,48 @@ + + \ No newline at end of file diff --git a/web/src/app/login/login.component.scss b/web/src/app/login/login.component.scss new file mode 100644 index 0000000..f7e5919 --- /dev/null +++ b/web/src/app/login/login.component.scss @@ -0,0 +1,18 @@ +.login-card{ + width: 20em; + // height: 30em; + flex-grow: 0 !important; +} + +.login-form-field{ + width: 100%; +} + +.login-instructions{ + margin-bottom: 1em; + font-size: 85%; +} +.login-buttons{ + display: flex; + flex-direction: row-reverse; +} \ No newline at end of file diff --git a/web/src/app/login/login.component.spec.ts b/web/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..18f3685 --- /dev/null +++ b/web/src/app/login/login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoginComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/login/login.component.ts b/web/src/app/login/login.component.ts new file mode 100644 index 0000000..770f2b5 --- /dev/null +++ b/web/src/app/login/login.component.ts @@ -0,0 +1,105 @@ + +import {ChangeDetectionStrategy, Component, signal} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { HostBinding } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { SafeStyle } from '@angular/platform-browser'; +import { ConfigService } from '../services/config-service'; +import {MatButtonModule} from '@angular/material/button'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatIconModule} from '@angular/material/icon'; +import {MatInputModule} from '@angular/material/input'; +import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; +import {ReactiveFormsModule} from '@angular/forms'; +import { Router, RouterOutlet } from '@angular/router'; +import { LoginService } from '../services/login.service'; +import { stringify } from 'querystring'; +import { BehaviorSubject, Observable } from 'rxjs'; + +@Component({ + selector: 'app-login', + standalone: true, + imports: [CommonModule,MatFormFieldModule, MatInputModule, MatButtonModule, MatIconModule,ReactiveFormsModule, RouterOutlet], + templateUrl: './login.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + styleUrl: './login.component.scss' +}) +export class LoginComponent { + + hide = signal(true); + loginFormGroup!: FormGroup; + errorMessage: string = ""; + showError: BehaviorSubject = new BehaviorSubject(false); + + + clickEvent(event: MouseEvent) { + this.hide.set(!this.hide()); + event.stopPropagation(); + } + + constructor( + public configSvc: ConfigService, + public sanitizer: DomSanitizer, + private router: Router, + public loginSvc: LoginService, + ){ + if(this.loginSvc.isLoggedIn()){ + + this.router.navigate(['/' + "main"]); + } + this.loginFormGroup = new FormGroup({ + userName: new FormControl('JohnDoe',[Validators.required]), + password: new FormControl('password',[Validators.required]), + }); + console.log(this.loginFormGroup) + } + + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('display: flex; flex-grow: 1'); + } + + + validLogin(){ + if(this.loginFormGroup.valid){ + console.log("valid") + return true; + } else { + console.log("invalid") + return false; + } + } + + async login(){ + if(!this.validLogin()){ + this.showErrorTimer(this.configSvc.errorTime); + return; + } + console.log("start login") + var username = this.loginFormGroup.get("userName")?.value + var password = this.loginFormGroup.get("password")?.value + var result = await this.loginSvc.login(username,password); + if(!result.success){ + console.log("login call failed") + this.errorMessage = result.errorMessage; + this.showErrorTimer(this.configSvc.errorTime); + return; + } + + + this.router.navigate(['/' + "main"]); + + } + register(){ + console.log("register") + } + async showErrorTimer(ms: number){ + this.showError.next(true); + return setTimeout(() => { + console.log("error timer hit") + this.showError.next(false) + this.errorMessage = ""; + console.log(this.showError) + },ms); + } +} diff --git a/web/src/app/main-diagram/main-diagram.component.html b/web/src/app/main-diagram/main-diagram.component.html new file mode 100644 index 0000000..db1a63a --- /dev/null +++ b/web/src/app/main-diagram/main-diagram.component.html @@ -0,0 +1 @@ +

main-diagram works!

diff --git a/web/src/app/main-diagram/main-diagram.component.scss b/web/src/app/main-diagram/main-diagram.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/main-diagram/main-diagram.component.spec.ts b/web/src/app/main-diagram/main-diagram.component.spec.ts new file mode 100644 index 0000000..8834581 --- /dev/null +++ b/web/src/app/main-diagram/main-diagram.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MainDiagramComponent } from './main-diagram.component'; + +describe('MainDiagramComponent', () => { + let component: MainDiagramComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MainDiagramComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MainDiagramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/main-diagram/main-diagram.component.ts b/web/src/app/main-diagram/main-diagram.component.ts new file mode 100644 index 0000000..7c23ab0 --- /dev/null +++ b/web/src/app/main-diagram/main-diagram.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-main-diagram', + standalone: true, + imports: [], + templateUrl: './main-diagram.component.html', + styleUrl: './main-diagram.component.scss' +}) +export class MainDiagramComponent { + +} diff --git a/web/src/app/not-found/not-found.component.html b/web/src/app/not-found/not-found.component.html new file mode 100644 index 0000000..8071020 --- /dev/null +++ b/web/src/app/not-found/not-found.component.html @@ -0,0 +1 @@ +

not-found works!

diff --git a/web/src/app/not-found/not-found.component.scss b/web/src/app/not-found/not-found.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/not-found/not-found.component.spec.ts b/web/src/app/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..5b65d9e --- /dev/null +++ b/web/src/app/not-found/not-found.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NotFoundComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/not-found/not-found.component.ts b/web/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..01bdc09 --- /dev/null +++ b/web/src/app/not-found/not-found.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-not-found', + standalone: true, + imports: [], + templateUrl: './not-found.component.html', + styleUrl: './not-found.component.scss' +}) +export class NotFoundComponent { + +} diff --git a/web/src/app/password-restore/password-restore.component.html b/web/src/app/password-restore/password-restore.component.html new file mode 100644 index 0000000..edf4665 --- /dev/null +++ b/web/src/app/password-restore/password-restore.component.html @@ -0,0 +1 @@ +

password-restore works!

diff --git a/web/src/app/password-restore/password-restore.component.scss b/web/src/app/password-restore/password-restore.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/password-restore/password-restore.component.spec.ts b/web/src/app/password-restore/password-restore.component.spec.ts new file mode 100644 index 0000000..08389b3 --- /dev/null +++ b/web/src/app/password-restore/password-restore.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PasswordRestoreComponent } from './password-restore.component'; + +describe('PasswordRestoreComponent', () => { + let component: PasswordRestoreComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PasswordRestoreComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PasswordRestoreComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/password-restore/password-restore.component.ts b/web/src/app/password-restore/password-restore.component.ts new file mode 100644 index 0000000..2e055b6 --- /dev/null +++ b/web/src/app/password-restore/password-restore.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-password-restore', + standalone: true, + imports: [], + templateUrl: './password-restore.component.html', + styleUrl: './password-restore.component.scss' +}) +export class PasswordRestoreComponent { + +} diff --git a/web/src/app/register/register.component.html b/web/src/app/register/register.component.html new file mode 100644 index 0000000..6b0ba2e --- /dev/null +++ b/web/src/app/register/register.component.html @@ -0,0 +1 @@ +

register works!

diff --git a/web/src/app/register/register.component.scss b/web/src/app/register/register.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/register/register.component.spec.ts b/web/src/app/register/register.component.spec.ts new file mode 100644 index 0000000..757b895 --- /dev/null +++ b/web/src/app/register/register.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RegisterComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/register/register.component.ts b/web/src/app/register/register.component.ts new file mode 100644 index 0000000..141d7e8 --- /dev/null +++ b/web/src/app/register/register.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-register', + standalone: true, + imports: [], + templateUrl: './register.component.html', + styleUrl: './register.component.scss' +}) +export class RegisterComponent { + +} diff --git a/web/src/app/services/auth-service.ts b/web/src/app/services/auth-service.ts new file mode 100644 index 0000000..453000d --- /dev/null +++ b/web/src/app/services/auth-service.ts @@ -0,0 +1,52 @@ +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ConfigService } from './config-service'; + + +@Injectable() +export class AuthService { + + + constructor( + private http: HttpClient, + private router: Router, + private configSvc: ConfigService, + private route: ActivatedRoute, + ) { + } + + userId(): number { + return parseInt(localStorage.getItem('userId') ?? "0", 10); + } + + getUsers(){ + return this.http.get(this.configSvc.apiUrl + 'user/getUsers'); + } + getUserDetail(id :number){ + return this.http.get(this.configSvc.apiUrl + 'user/getUserDetail/' + id); + } + getRoles(){ + return this.http.get(this.configSvc.apiUrl + 'user/getRoles/'); + } + getRoleDetail(id : number){ + return this.http.get(this.configSvc.apiUrl + 'user/getRoleDetail/' + id); + } + + createRole(role: any){ + return this.http.post(this.configSvc.apiUrl + 'user/createRole/', { roleName: role.roleName }) + } + updateUser(user: any){ + return this.http.post(this.configSvc.apiUrl + 'user/updateUser', user) + } + updateRole(role: any){ + return this.http.post(this.configSvc.apiUrl + 'user/updateRole', role) + } + deleteRole(roleid: any){ + return this.http.post(this.configSvc.apiUrl + 'user/deleteRole', roleid) + } + + + + +} diff --git a/web/src/app/services/authGuards/auth-guard-admin.ts b/web/src/app/services/authGuards/auth-guard-admin.ts new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/services/authGuards/auth-guard-general.ts b/web/src/app/services/authGuards/auth-guard-general.ts new file mode 100644 index 0000000..43543aa --- /dev/null +++ b/web/src/app/services/authGuards/auth-guard-general.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, CanActivateChild, CanDeactivate, CanLoad, Router } from '@angular/router'; +import { LoginService } from '../login.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuardGeneral implements CanActivate { + constructor(private loginSvc: LoginService, private router: Router) {} + + canActivate(): boolean { + return this.checkAuth(); + } + + canActivateChild(): boolean { + return this.checkAuth(); + } + + canLoad(): boolean { + return this.checkAuth(); + } + + private checkAuth(): boolean { + if (this.loginSvc.isLoggedIn()) { + return true; + } else { + // Redirect to the login page if the user is not authenticated + localStorage.removeItem("userToken") + this.router.navigate(['/login/signin']); + return false; + } + } + +} \ No newline at end of file diff --git a/web/src/app/services/config-service.ts b/web/src/app/services/config-service.ts new file mode 100644 index 0000000..b12aba7 --- /dev/null +++ b/web/src/app/services/config-service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, APP_INITIALIZER } from '@angular/core'; +import { environemnt } from '../../environments/environment'; + + + +@Injectable() +export class ConfigService { + + apiUrl!: string; + configUrl!: string; + offline!: boolean;; + errorTime!: number; + tokenTimeOutDefault !: number; + + + + constructor(private http: HttpClient) { + console.log("config conrtuctor") + this.loadConfig(); + + } + + /** + * + */ + loadConfig() { + this.configUrl = '/assets/config.json'; + + this.apiUrl = + environemnt.api.protocol + "//" + + environemnt.api.url + ":" + + environemnt.api.port + + this.offline = environemnt.offlineTesting; + this.errorTime = environemnt.errorMessageTime; + this.tokenTimeOutDefault = environemnt.tokenTimeOutDefault; + // return this.http.get(this.configUrl) + // .toPromise() + // .then((data: any) => { + // console.log(data) + // let apiPort = data.api.port != "" ? ":" + data.api.port : ""; + // let apiProtocol = data.api.protocol + "://"; + // if (localStorage.getItem("apiUrl") != null) { + // this.apiUrl = localStorage.getItem("apiUrl") + "/" + data.api.apiIdentifier + "/"; + // } else { + // console.log(data) + // this.apiUrl = apiProtocol + data.api.url + apiPort + "/" + data.api.apiIdentifier + "/"; + // } + // console.log(this.apiUrl) + + // }).catch(error => console.log('Failed to load config file: ' + (error).message)); + // } + } +} diff --git a/web/src/app/services/dashboard-service.ts b/web/src/app/services/dashboard-service.ts new file mode 100644 index 0000000..e02217a --- /dev/null +++ b/web/src/app/services/dashboard-service.ts @@ -0,0 +1,19 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, APP_INITIALIZER } from '@angular/core'; +import { ConfigService } from './config-service'; + + + +@Injectable() +export class DashService { + + + + constructor(private config: ConfigService) { + } + + loadDashBoard(){ + console.log("Using " + this.config.apiUrl + "call to load dashboard") + } + +} diff --git a/web/src/app/services/login.service.ts b/web/src/app/services/login.service.ts new file mode 100644 index 0000000..b8b9936 --- /dev/null +++ b/web/src/app/services/login.service.ts @@ -0,0 +1,89 @@ +import { afterNextRender, Injectable } from '@angular/core'; +import { ConfigService } from './config-service'; +import { HttpClient } from '@angular/common/http'; +import { Router, RouterOutlet } from '@angular/router'; +import { httpReponseContext } from '../../models/httpReponseContext'; +import { userToken } from '../../models/userTokenModel'; + +@Injectable({ + providedIn: 'root' +}) +export class LoginService { + + + constructor( + private configSvc: ConfigService, + private router: Router, + private http: HttpClient, + ) { + } + + isLoggedIn(){ + var userTokenString = localStorage.getItem("userToken"); + if(userTokenString == null) return false; + var userToken = JSON.parse(userTokenString); + if(!this.tokenValid(userToken)){ + return false; + } + return true; + } + + tokenValid(token: userToken){ + //TODO: check actual JWT token for validity, currently testing token timeout for offline testing purposes + //if(token.token.isValid){return true;} + var now = new Date().getTime(); + var exp = new Date(token.expTime).getTime(); + console.log(exp) + console.log(now) + if(exp < now){ + console.log("Token timeout hit") + console.log(token) + console.log(new Date()) + return false; + } else { + console.log(token) + console.log(new Date()) + return true; + } + } + async login(username : string, password: string){ + var retVal = new httpReponseContext(false,{},"dafault login error, check code for issue"); + if(this.configSvc.offline){ + console.log(password) + if(username == "JohnDoe" && password == "password"){ + var retVal = new httpReponseContext(true,{},"test"); + retVal.success = true + localStorage.setItem("userToken",JSON.stringify(this.genUserToken(username,{}))); + localStorage.setItem("userName",username); + return retVal; + } else { + return new httpReponseContext(false,{},"Invalid username / password combination. In Offline mode please use JohnDoe and password") + } + } else { + this.http.get(this.configSvc.apiUrl + 'user/getUsers').subscribe({ + next: (v) => console.log(v), + error: (e) => console.error(e), + complete: () => console.info('complete') + }) + } + return retVal; + } + + genUserToken(username : string, token: Object){ + var retval = new userToken(); + var date = new Date(); + retval.expTime = new Date(date.getTime() + this.configSvc.tokenTimeOutDefault); + retval.username = username; + var token = token; + return retval; + } + clearUserToken(){ + localStorage.removeItem("userToken") + localStorage.removeItem("userName") + } + + logout(){ + this.clearUserToken(); + this.router.navigate(['/login/signin']); + } +} diff --git a/web/src/app/services/storage-service.ts b/web/src/app/services/storage-service.ts new file mode 100644 index 0000000..7205181 --- /dev/null +++ b/web/src/app/services/storage-service.ts @@ -0,0 +1,18 @@ +import { inject, Injectable } from '@angular/core'; +import { LOCAL_STORAGE } from '../tokens'; +@Injectable({ + providedIn: 'root', +}) +export class StorageService { + private storage = inject(LOCAL_STORAGE); + setItem(key: string, value: any): void { + this.storage.setItem(key, JSON.stringify(value)); + } + getItem(key: string): T | null { + const storedValue = this.storage.getItem(key); + return storedValue ? (JSON.parse(storedValue) as T) : null; + } + removeItem(key: string): void { + this.storage.removeItem(key); + } +} \ No newline at end of file diff --git a/web/src/app/tokens/index.ts b/web/src/app/tokens/index.ts new file mode 100644 index 0000000..d4fce5a --- /dev/null +++ b/web/src/app/tokens/index.ts @@ -0,0 +1,3 @@ + +import { InjectionToken } from '@angular/core'; +export const LOCAL_STORAGE = new InjectionToken('Local Storage'); \ No newline at end of file diff --git a/web/src/app/user-management/user-management.component.html b/web/src/app/user-management/user-management.component.html new file mode 100644 index 0000000..1ca51cf --- /dev/null +++ b/web/src/app/user-management/user-management.component.html @@ -0,0 +1 @@ +

user-management works!

diff --git a/web/src/app/user-management/user-management.component.scss b/web/src/app/user-management/user-management.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/user-management/user-management.component.spec.ts b/web/src/app/user-management/user-management.component.spec.ts new file mode 100644 index 0000000..5a81f8c --- /dev/null +++ b/web/src/app/user-management/user-management.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserManagementComponent } from './user-management.component'; + +describe('UserManagementComponent', () => { + let component: UserManagementComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UserManagementComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UserManagementComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/user-management/user-management.component.ts b/web/src/app/user-management/user-management.component.ts new file mode 100644 index 0000000..c503d96 --- /dev/null +++ b/web/src/app/user-management/user-management.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-user-management', + standalone: true, + imports: [], + templateUrl: './user-management.component.html', + styleUrl: './user-management.component.scss' +}) +export class UserManagementComponent { + +} diff --git a/web/src/assets/config.json b/web/src/assets/config.json new file mode 100644 index 0000000..b8c4f32 --- /dev/null +++ b/web/src/assets/config.json @@ -0,0 +1,24 @@ +{ + "appCode": "ResDeeds", + "app":{ + "protocol": "http", + "url": "localhost", + "port":"4200" + }, + "api":{ + "protocol":"http", + "url": "localhost", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "versionApi":{ + "protocol":"http", + "host": "api", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "helpContactEmail": "dylan.johnson@inl.gov", + "defaultPasswordSubject": "Your password has been reset" +} diff --git a/web/src/environments/environment.prod.ts b/web/src/environments/environment.prod.ts new file mode 100644 index 0000000..0db265c --- /dev/null +++ b/web/src/environments/environment.prod.ts @@ -0,0 +1,27 @@ +export const environemnt = { + "appCode": "ResDeeds", + "offlineTesting": false, + "errorMessageTime": 2000, + "tokenTimeOutDefault": 5 * 60000, //Value * milliseconds = amount of minutes to timeout + "app":{ + "protocol": "http", + "url": "localhost", + "port":"4200" + }, + "api":{ + "protocol":"http", + "url": "localhost", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "versionApi":{ + "protocol":"http", + "host": "api", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "helpContactEmail": "dylan.johnson@inl.gov", + "defaultPasswordSubject": "Your password has been reset" +} diff --git a/web/src/environments/environment.ts b/web/src/environments/environment.ts new file mode 100644 index 0000000..dc99224 --- /dev/null +++ b/web/src/environments/environment.ts @@ -0,0 +1,27 @@ +export const environemnt = { + "appCode": "ResDeeds", + "offlineTesting": true, + "errorMessageTime": 2000, + "tokenTimeOutDefault": 5 * 60000, //Value * milliseconds = amount of minutes to timeout + "app":{ + "protocol": "http", + "url": "localhost", + "port":"4200" + }, + "api":{ + "protocol":"http", + "url": "localhost", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "versionApi":{ + "protocol":"http", + "host": "api", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "helpContactEmail": "dylan.johnson@inl.gov", + "defaultPasswordSubject": "Your password has been reset" +} diff --git a/web/src/index.html b/web/src/index.html index 00e4e9c..e7c11ec 100644 --- a/web/src/index.html +++ b/web/src/index.html @@ -1,13 +1,14 @@ - - + + + Resdeeds - + diff --git a/web/src/models/httpReponseContext.ts b/web/src/models/httpReponseContext.ts new file mode 100644 index 0000000..1c4444c --- /dev/null +++ b/web/src/models/httpReponseContext.ts @@ -0,0 +1,11 @@ +export class httpReponseContext { + success: boolean = false; + messageBody: any = null; + errorMessage: string = "Error, please define error message if needed" + + constructor(success: boolean = false, messageBody: any = {}, errorMessage: any = "Error Not Defined"){ + this.success = success, + this.messageBody = messageBody + this.errorMessage = errorMessage + }; +} \ No newline at end of file diff --git a/web/src/models/userTokenModel.ts b/web/src/models/userTokenModel.ts new file mode 100644 index 0000000..ead5c39 --- /dev/null +++ b/web/src/models/userTokenModel.ts @@ -0,0 +1,8 @@ +export class userToken { + username: string = ""; + token: any = null; + expTime: Date = new Date(); + + constructor(){ + }; +} \ No newline at end of file diff --git a/web/src/styles.scss b/web/src/styles.scss index 90d4ee0..d8bcb98 100644 --- a/web/src/styles.scss +++ b/web/src/styles.scss @@ -1 +1,175 @@ /* You can add global styles to this file, and also import other style files */ + +//================Base Colors=========================== + +:root{ + --primary-color: #507d69; + --secondary-color:#6ead90; + --tertiary-color: #738A80; + --tertiary-color-dark: #4B5043; + --button-main-color: #218263; + --button-secondary-color: var(--white); + --link: #2b7dcf; + --black: #090909; + --white: #f0f0f0; + --light-gray: #e0e0e0; + --card-background: #d0d0d0; + + --card-spacing: .2em; +} + +.color-primary{ + background-color: var(--primary-color); + color: var(--black); +} + +.color-secondary{ + +} + +.color-background{ + background-color: var(--light-gray); +} + + +//================General Layout======================== + +//Flex +.flex{ + display: flex; +} + + +//Width decimal +.w-100{ + width: 100%; +} +.w-75{ + width: 75%; +} +.w-50{ + width: 50%; +} +.w-25{ + width: 25%; +} +.w-10{ + width: 10%; +} +//Width Duodecimal +.w-1{ + width: 8.3%; +} +.w-2{ + width: 16.6%; +} +.w-3{ + width: 25%; +} +.w-4{ + width: 33.2%; +} +.w-5{ + width: 41.6%; +} +.w-6{ + width: 50%; +} +.w-7{ + width: 58.3%; +} +.w-8{ + width: 66.7%; +} +.w-9{ + width: 75%; +} +.w-10{ + width: 83.3%; +} +.w-11{ + width: 91.6%; +} +.w-12{ + width: 100%; +} + +.centered { + margin: auto; +} + +.clickable-text{ + color: var(--link); + cursor: pointer;; +} +.clickable-text:hover{ + text-decoration: underline; +} + +.btn-main{ + background-color: var(--button-main-color) !important; + color: var(--white) !important; +} +.btn-secondary{ + background-color: var(--button-secondary-color) !important; + color: var(--black) !important; +} + +.card-row{ + display: flex; + flex-direction: row; + margin-bottom: 1em; + flex-basis: 95%; + max-height: 33%; +} + +.card{ + display: flex; + flex-grow: 1; + flex-direction: column; + background-color: var(--card-background); + font-size: 150%; + border-radius: 20px; +} + +.card-header{ + border-radius: 20px 20px 0 0 ; + width:100; + padding: .45em .75em; + background-color: var(--tertiary-color); + color: car(--tertiary-color-dark); +} +.card-body{ + padding: 1em; + display: flex; + padding: 1em; + flex-grow: 1; + height: calc(100% - 4em); +} +.card-item { + padding: 1em; + font-size: 70%;; +} + + +.card-row > .card{ + margin: 0 var(--card-spacing); +} +.card-row > .card:first-child{ + margin-left: 0em; +} +.card-row > .card:last-child{ + margin-right: 0em; +} + +// .card-row .card:first{ +// margin-left: 0; +// } + +.clickable-mat-row{ + cursor: pointer; +} +.clickable-mat-row:hover{ + background-color: var(--light-gray); + cursor: pointer; +} \ No newline at end of file diff --git a/web/src/tsconfig.app.json b/web/src/tsconfig.app.json new file mode 100644 index 0000000..0607a80 --- /dev/null +++ b/web/src/tsconfig.app.json @@ -0,0 +1,19 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "baseUrl": "./", + "types": ["node"], + "experimentalDecorators": true, + "allowJs": true, + "target": "ES2022", + }, + "files": [ + "main.ts", + "polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] + } + \ No newline at end of file diff --git a/web/src/tsconfig.spec.json b/web/src/tsconfig.spec.json new file mode 100644 index 0000000..9fe4fdb --- /dev/null +++ b/web/src/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "baseUrl": "./", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts", + "polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] + } + \ No newline at end of file