From 479b5ff76e807219978f9812abfe0d9cde974022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Tue, 28 Oct 2025 10:17:30 +0100 Subject: [PATCH] location WIP --- src/main/angular/.editorconfig | 1 - src/main/angular/.gitignore | 1 + src/main/angular/README.md | 2 +- src/main/angular/package-lock.json | 1188 +++++------------ src/main/angular/package.json | 16 +- src/main/angular/src/app/COMMON.ts | 350 ----- src/main/angular/src/app/app.config.ts | 8 +- src/main/angular/src/app/app.html | 22 +- src/main/angular/src/app/app.less | 54 - src/main/angular/src/app/app.routes.ts | 13 +- src/main/angular/src/app/app.ts | 7 +- src/main/angular/src/app/common.ts | 451 +++++++ src/main/angular/src/app/common/Value.ts | 29 - .../common/checkbox/checkbox.component.html | 1 - .../app/common/checkbox/checkbox.component.ts | 33 - .../app/common/column/column.component.html | 12 - .../app/common/column/column.component.less | 3 - .../src/app/common/column/column.component.ts | 30 - .../app/common/number/number.component.html | 1 - .../app/common/number/number.component.less | 0 .../src/app/common/number/number.component.ts | 53 - .../common/numberNN/number-n-n.component.html | 1 - .../common/numberNN/number-n-n.component.less | 0 .../common/numberNN/number-n-n.component.ts | 56 - .../angular/src/app/common/sorter/Column.ts | 26 - .../src/app/common/sorter/Direction.ts | 4 - .../angular/src/app/common/sorter/Order.ts | 20 - .../angular/src/app/common/sorter/Sorter.ts | 65 - .../src/app/common/text/text.component.html | 1 - .../src/app/common/text/text.component.less | 0 .../src/app/common/text/text.component.ts | 51 - src/main/angular/src/app/config/Config.ts | 27 - .../src/app/config/SeriesListConfig.ts | 34 - .../angular/src/app/config/TopicListConfig.ts | 25 - .../angular/src/app/config/config.service.ts | 24 - .../app/dashboard/dashboard.component.html | 10 - .../app/dashboard/dashboard.component.less | 12 - .../src/app/dashboard/dashboard.component.ts | 28 - src/main/angular/src/app/location/Location.ts | 23 + .../src/app/location/list/location-list.html | 7 + .../list/location-list.less} | 0 .../src/app/location/list/location-list.ts | 25 + .../src/app/location/location-service.ts | 18 + src/main/angular/src/app/plot/Group.ts | 11 - src/main/angular/src/app/plot/Plot.ts | 44 - src/main/angular/src/app/plot/axis/Axis.ts | 45 - .../angular/src/app/plot/axis/graph/Graph.ts | 74 - .../src/app/plot/axis/graph/GraphType.ts | 32 - .../plot/editor/plot-editor.component.html | 244 ---- .../plot/editor/plot-editor.component.less | 43 - .../app/plot/editor/plot-editor.component.ts | 145 -- src/main/angular/src/app/plot/plot.service.ts | 165 --- .../src/app/plot/plot/plot.component.html | 3 - .../src/app/plot/plot/plot.component.less | 4 - .../src/app/plot/plot/plot.component.ts | 275 ---- .../src/app/series/AllSeriesPointRequest.ts | 27 - .../src/app/series/AllSeriesPointResponse.ts | 74 - src/main/angular/src/app/series/Interval.ts | 41 - src/main/angular/src/app/series/MinMaxAvg.ts | 92 -- src/main/angular/src/app/series/Series.ts | 61 - .../angular/src/app/series/SeriesPoint.ts | 40 - .../angular/src/app/series/SeriesSorter.ts | 45 - src/main/angular/src/app/series/SeriesType.ts | 5 - src/main/angular/src/app/series/bool/Bool.ts | 26 - .../src/app/series/bool/bool-service.ts | 16 - .../angular/src/app/series/delta/Delta.ts | 24 - .../src/app/series/delta/delta-service.ts | 16 - .../src/app/series/delta/meter/Meter.ts | 24 - .../series/list/series-list.component.html | 83 -- .../series/list/series-list.component.less | 93 -- .../app/series/list/series-list.component.ts | 155 --- .../angular/src/app/series/series.service.ts | 39 - .../angular/src/app/series/varying/Varying.ts | 26 - .../src/app/series/varying/varying-service.ts | 16 - .../angular/src/app/topic/TimestampType.ts | 4 - src/main/angular/src/app/topic/Topic.ts | 48 - src/main/angular/src/app/topic/TopicQuery.ts | 28 - .../src/app/topic/TopicQueryFunction.ts | 5 - .../app/topic/list/topic-list.component.html | 46 - .../app/topic/list/topic-list.component.less | 112 -- .../app/topic/list/topic-list.component.ts | 94 -- .../angular/src/app/topic/topic.service.ts | 16 - .../angular/src/app/weather/WeatherHour.ts | 25 - .../app/weather/plot/weather-component.html | 3 - .../app/weather/plot/weather-component.less | 4 - .../src/app/weather/plot/weather-component.ts | 204 --- .../src/app/weather/weather-service.ts | 20 - src/main/angular/src/config.less | 2 - src/main/angular/src/index.html | 4 +- src/main/angular/src/main.ts | 6 +- src/main/angular/src/styles.less | 52 +- 91 files changed, 903 insertions(+), 4490 deletions(-) delete mode 100644 src/main/angular/src/app/COMMON.ts create mode 100644 src/main/angular/src/app/common.ts delete mode 100644 src/main/angular/src/app/common/Value.ts delete mode 100644 src/main/angular/src/app/common/checkbox/checkbox.component.html delete mode 100644 src/main/angular/src/app/common/checkbox/checkbox.component.ts delete mode 100644 src/main/angular/src/app/common/column/column.component.html delete mode 100644 src/main/angular/src/app/common/column/column.component.less delete mode 100644 src/main/angular/src/app/common/column/column.component.ts delete mode 100644 src/main/angular/src/app/common/number/number.component.html delete mode 100644 src/main/angular/src/app/common/number/number.component.less delete mode 100644 src/main/angular/src/app/common/number/number.component.ts delete mode 100644 src/main/angular/src/app/common/numberNN/number-n-n.component.html delete mode 100644 src/main/angular/src/app/common/numberNN/number-n-n.component.less delete mode 100644 src/main/angular/src/app/common/numberNN/number-n-n.component.ts delete mode 100644 src/main/angular/src/app/common/sorter/Column.ts delete mode 100644 src/main/angular/src/app/common/sorter/Direction.ts delete mode 100644 src/main/angular/src/app/common/sorter/Order.ts delete mode 100644 src/main/angular/src/app/common/sorter/Sorter.ts delete mode 100644 src/main/angular/src/app/common/text/text.component.html delete mode 100644 src/main/angular/src/app/common/text/text.component.less delete mode 100644 src/main/angular/src/app/common/text/text.component.ts delete mode 100644 src/main/angular/src/app/config/Config.ts delete mode 100644 src/main/angular/src/app/config/SeriesListConfig.ts delete mode 100644 src/main/angular/src/app/config/TopicListConfig.ts delete mode 100644 src/main/angular/src/app/config/config.service.ts delete mode 100644 src/main/angular/src/app/dashboard/dashboard.component.html delete mode 100644 src/main/angular/src/app/dashboard/dashboard.component.less delete mode 100644 src/main/angular/src/app/dashboard/dashboard.component.ts create mode 100644 src/main/angular/src/app/location/Location.ts create mode 100644 src/main/angular/src/app/location/list/location-list.html rename src/main/angular/src/app/{common/checkbox/checkbox.component.less => location/list/location-list.less} (100%) create mode 100644 src/main/angular/src/app/location/list/location-list.ts create mode 100644 src/main/angular/src/app/location/location-service.ts delete mode 100644 src/main/angular/src/app/plot/Group.ts delete mode 100644 src/main/angular/src/app/plot/Plot.ts delete mode 100644 src/main/angular/src/app/plot/axis/Axis.ts delete mode 100644 src/main/angular/src/app/plot/axis/graph/Graph.ts delete mode 100644 src/main/angular/src/app/plot/axis/graph/GraphType.ts delete mode 100644 src/main/angular/src/app/plot/editor/plot-editor.component.html delete mode 100644 src/main/angular/src/app/plot/editor/plot-editor.component.less delete mode 100644 src/main/angular/src/app/plot/editor/plot-editor.component.ts delete mode 100644 src/main/angular/src/app/plot/plot.service.ts delete mode 100644 src/main/angular/src/app/plot/plot/plot.component.html delete mode 100644 src/main/angular/src/app/plot/plot/plot.component.less delete mode 100644 src/main/angular/src/app/plot/plot/plot.component.ts delete mode 100644 src/main/angular/src/app/series/AllSeriesPointRequest.ts delete mode 100644 src/main/angular/src/app/series/AllSeriesPointResponse.ts delete mode 100644 src/main/angular/src/app/series/Interval.ts delete mode 100644 src/main/angular/src/app/series/MinMaxAvg.ts delete mode 100644 src/main/angular/src/app/series/Series.ts delete mode 100644 src/main/angular/src/app/series/SeriesPoint.ts delete mode 100644 src/main/angular/src/app/series/SeriesSorter.ts delete mode 100644 src/main/angular/src/app/series/SeriesType.ts delete mode 100644 src/main/angular/src/app/series/bool/Bool.ts delete mode 100644 src/main/angular/src/app/series/bool/bool-service.ts delete mode 100644 src/main/angular/src/app/series/delta/Delta.ts delete mode 100644 src/main/angular/src/app/series/delta/delta-service.ts delete mode 100644 src/main/angular/src/app/series/delta/meter/Meter.ts delete mode 100644 src/main/angular/src/app/series/list/series-list.component.html delete mode 100644 src/main/angular/src/app/series/list/series-list.component.less delete mode 100644 src/main/angular/src/app/series/list/series-list.component.ts delete mode 100644 src/main/angular/src/app/series/series.service.ts delete mode 100644 src/main/angular/src/app/series/varying/Varying.ts delete mode 100644 src/main/angular/src/app/series/varying/varying-service.ts delete mode 100644 src/main/angular/src/app/topic/TimestampType.ts delete mode 100644 src/main/angular/src/app/topic/Topic.ts delete mode 100644 src/main/angular/src/app/topic/TopicQuery.ts delete mode 100644 src/main/angular/src/app/topic/TopicQueryFunction.ts delete mode 100644 src/main/angular/src/app/topic/list/topic-list.component.html delete mode 100644 src/main/angular/src/app/topic/list/topic-list.component.less delete mode 100644 src/main/angular/src/app/topic/list/topic-list.component.ts delete mode 100644 src/main/angular/src/app/topic/topic.service.ts delete mode 100644 src/main/angular/src/app/weather/WeatherHour.ts delete mode 100644 src/main/angular/src/app/weather/plot/weather-component.html delete mode 100644 src/main/angular/src/app/weather/plot/weather-component.less delete mode 100644 src/main/angular/src/app/weather/plot/weather-component.ts delete mode 100644 src/main/angular/src/app/weather/weather-service.ts delete mode 100644 src/main/angular/src/config.less diff --git a/src/main/angular/.editorconfig b/src/main/angular/.editorconfig index be29070..f166060 100644 --- a/src/main/angular/.editorconfig +++ b/src/main/angular/.editorconfig @@ -9,7 +9,6 @@ insert_final_newline = true trim_trailing_whitespace = true [*.ts] -# noinspection EditorConfigKeyCorrectness quote_type = single ij_typescript_use_double_quotes = false diff --git a/src/main/angular/.gitignore b/src/main/angular/.gitignore index cc7b141..b1d225e 100644 --- a/src/main/angular/.gitignore +++ b/src/main/angular/.gitignore @@ -36,6 +36,7 @@ yarn-error.log /libpeerconnection.log testem.log /typings +__screenshots__/ # System files .DS_Store diff --git a/src/main/angular/README.md b/src/main/angular/README.md index 22dacb0..5efe040 100644 --- a/src/main/angular/README.md +++ b/src/main/angular/README.md @@ -1,6 +1,6 @@ # Angular -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.1. +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.7. ## Development server diff --git a/src/main/angular/package-lock.json b/src/main/angular/package-lock.json index 3d964f8..69341a0 100644 --- a/src/main/angular/package-lock.json +++ b/src/main/angular/package-lock.json @@ -14,21 +14,15 @@ "@angular/forms": "^20.3.0", "@angular/platform-browser": "^20.3.0", "@angular/router": "^20.3.0", - "@fortawesome/angular-fontawesome": "^3.0.0", - "@fortawesome/free-regular-svg-icons": "^7.0.0", - "@fortawesome/free-solid-svg-icons": "^7.0.0", "@stomp/ng2-stompjs": "^8.0.0", - "@stomp/stompjs": "^7.2.0", - "chartjs-adapter-date-fns": "^3.0.0", - "date-fns": "^4.1.0", - "ng2-charts": "^8.0.0", + "@stomp/stompjs": "^7.2.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, "devDependencies": { - "@angular/build": "^20.3.1", - "@angular/cli": "^20.3.1", + "@angular/build": "^20.3.7", + "@angular/cli": "^20.3.7", "@angular/compiler-cli": "^20.3.0", "@types/jasmine": "~5.1.0", "jasmine-core": "~5.9.0", @@ -265,13 +259,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.2003.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.1.tgz", - "integrity": "sha512-PE/yMVv8RZ7nQzGROi0juZo+yMZE2QwyBXc9yFrHIRozuTzTFaMW/9ifCZDVrpicjyHEk3s+7hUVNCcKO/xIIQ==", + "version": "0.2003.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.7.tgz", + "integrity": "sha512-NGHLfrNQNjwWwvyQomMM1AqRaqH3UU0TwySJh9XlSc9dC/roB5zD2NjLf98K4LfAIfHvDBwkQ+dMo3F556/Xuw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.1", + "@angular-devkit/core": "20.3.7", "rxjs": "7.8.2" }, "engines": { @@ -281,9 +275,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.1.tgz", - "integrity": "sha512-TmS69GqBlbTfydn7C4tUKr0mshYSStuCkgruXbvedHFX8+7XBp8wPE+VUzdKnSmKZi6buI4oskDbJ1AdGtNm/g==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.7.tgz", + "integrity": "sha512-psmcjwYcXve4sLrcdnARc15/Wfd3RpydbtLo9+mViNzk5HQ6L2eEztKl/2QVYMgzZVIa1GfhjwUllVCyLAv3sg==", "dev": true, "license": "MIT", "dependencies": { @@ -309,13 +303,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.1.tgz", - "integrity": "sha512-uzMqcgOfcCBiYb+cbMJmgJL2C2d3uYFp6hU2ClYS8kRPXiA9sNVnvLmv4JrYJVLGQDejJtjPGIQrcmq11OQNLA==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.7.tgz", + "integrity": "sha512-DUxcQBPKO69p56ZgIdVfxWyLiSjdcUoD6BH9/nWHp0QiqRAR6GcXP4SFax76JPl2WsiCp4hHZ233Hf69AP1xew==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.1", + "@angular-devkit/core": "20.3.7", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "8.2.0", @@ -328,14 +322,14 @@ } }, "node_modules/@angular/build": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.1.tgz", - "integrity": "sha512-z5n8WnisyPrRvS1WctdDB3Svas0Wql1Eplnwh4O7waZHeJTOcd8zZeFxPbPGp12ybGf3HEEjTeWOigm1kRgW9g==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.7.tgz", + "integrity": "sha512-NHN5JNDqUc0Ux4IZPCe/fpFAnuRHujkxVfRHSqDFW5+jtj2JuW1XO6qlX+kDheFRlj/NvFgTpidKsE9IjpfMWQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2003.1", + "@angular-devkit/architect": "0.2003.7", "@babel/core": "7.28.3", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -353,12 +347,12 @@ "parse5-html-rewriting-stream": "8.0.0", "picomatch": "4.0.3", "piscina": "5.1.3", - "rolldown": "1.0.0-beta.32", + "rollup": "4.52.3", "sass": "1.90.0", "semver": "7.7.2", "source-map-support": "0.5.21", "tinyglobby": "0.2.14", - "vite": "7.1.5", + "vite": "7.1.11", "watchpack": "2.4.4" }, "engines": { @@ -377,7 +371,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.3.1", + "@angular/ssr": "^20.3.7", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^20.0.0", @@ -426,36 +420,20 @@ } } }, - "node_modules/@angular/cdk": { - "version": "20.2.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.3.tgz", - "integrity": "sha512-gu1zzxxcwobeiH21VpphM+cPFrQX0dxGwlFx1W8eTcLYLWd9YjlTETucBrEUEWcXmRrVTXf/VcqA0rWsxd50Ow==", - "license": "MIT", - "peer": true, - "dependencies": { - "parse5": "^8.0.0", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": "^20.0.0 || ^21.0.0", - "@angular/core": "^20.0.0 || ^21.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, "node_modules/@angular/cli": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.1.tgz", - "integrity": "sha512-TqhuDecbfAQgRDYPfpRQG9ZuTqb1DOeU7oQAYxpz9m/a7A2xqeNFLuCwwz8rqEPZB79/9r5ja0Gs1J4i080U0Q==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.7.tgz", + "integrity": "sha512-hNurF7g/e9cDHFBRCKLPSmQJs0n28jZsC3sTl/XuWE8PYtv5egh2EuqrxdruYB5GdANpIqSQNgDGQJrKrk/XnQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2003.1", - "@angular-devkit/core": "20.3.1", - "@angular-devkit/schematics": "20.3.1", + "@angular-devkit/architect": "0.2003.7", + "@angular-devkit/core": "20.3.7", + "@angular-devkit/schematics": "20.3.7", "@inquirer/prompts": "7.8.2", "@listr2/prompt-adapter-inquirer": "3.0.1", "@modelcontextprotocol/sdk": "1.17.3", - "@schematics/angular": "20.3.1", + "@schematics/angular": "20.3.7", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.35.0", "ini": "5.0.0", @@ -478,9 +456,9 @@ } }, "node_modules/@angular/common": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.0.tgz", - "integrity": "sha512-Il0HqdRdrmI8ufLXd49EYaa/BPqfiSqe5uuKrDxhkAdbRXwCXWsxbO/n8AwilwWn3CKLOCrEXQYKwbcFW0nYQQ==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.7.tgz", + "integrity": "sha512-uf8dXYTJbedk/wudkt2MfbtvN/T97aEZBtOTq8/IFQQZ3722rag6D+Cg76e5hBccROOn+ueGJX2gpxz02phTwA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -489,14 +467,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.3.0", + "@angular/core": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.0.tgz", - "integrity": "sha512-DvGDusjsDhxIX+nDzihSCGo81Fa8y94KB/bh24eyPwJWV6b0OkawFSvVwzxx8prV0UnNkCN1S/UoZXmtVZGJ4A==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.7.tgz", + "integrity": "sha512-EouHO15dUsgnFArj0M25R8cOPVoUfiFYSt6iXnMO8+S4dY1fDEmbFqkW5smlP66HL5Gys59Nwb5inejfIWHrLw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -506,9 +484,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.0.tgz", - "integrity": "sha512-umnZzzKw9RqDVkotYIyupJiKXQpU8knehMUBT1G3QwdeHppC+d/opxISYTkQtY/4IUAsZFLMukWIr82as0DSmw==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.7.tgz", + "integrity": "sha512-viZwWlwc1BAqryRJE0Wq2WgAxDaW9fuwtYHYrOWnIn9sy9KemKmR6RmU9VRydrwUROOlqK49R9+RC1wQ6sYwqA==", "dev": true, "license": "MIT", "dependencies": { @@ -529,7 +507,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.0", + "@angular/compiler": "20.3.7", "typescript": ">=5.8 <6.0" }, "peerDependenciesMeta": { @@ -539,9 +517,9 @@ } }, "node_modules/@angular/core": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.0.tgz", - "integrity": "sha512-4uH2TAMm1nXqQ9lcZyyNkjcdQ0Fjcf9Hh0HYrhMOEV6GAUHvM2I8Vr2dSQ40p/UKLEfe9+cpZ78EPocqPQCG6A==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.7.tgz", + "integrity": "sha512-2UuYzC2A5SUtu33tYTN411Wk0WilA+2Uld/GP3O6mragw1O7v/M8pMFmbe9TR5Ah/abRJIocWGlNqeztZmQmrw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -550,7 +528,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.0", + "@angular/compiler": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" }, @@ -564,9 +542,9 @@ } }, "node_modules/@angular/forms": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.0.tgz", - "integrity": "sha512-/KGCZUskk8imxz2e47CKe5Ykh3eqEDop0b9YUkZTvJ/dY/cdFK89RAK2xUvOlyUr2mkcByzdzyOhHaM9XEaELg==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.7.tgz", + "integrity": "sha512-uOCGCoqXeAWIlQMWiIeed/W8g8h2tk91YemMI+Ce1VQ/36Xfft40Bouz4eKcvJV6kLXGygdpWjzFGz32CE+3Og==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -575,16 +553,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.0", - "@angular/core": "20.3.0", - "@angular/platform-browser": "20.3.0", + "@angular/common": "20.3.7", + "@angular/core": "20.3.7", + "@angular/platform-browser": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.0.tgz", - "integrity": "sha512-/KsgfxDwP7/KXGrLLSyg4+Xd8HxmHi5dVCu+xHfa3QjzVIvvZfWZLxQj7guRlDtg/mz+t0/OSKvSUZzOAfVzGQ==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.7.tgz", + "integrity": "sha512-AbLtyR7fVEGDYyrz95dP2pc69J5XIjLLsFNAuNQPzNX02WPoAxtrWrNY6UnTzGoSrCc5F52hiL2Uo6yPZTiJcg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -593,9 +571,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "20.3.0", - "@angular/common": "20.3.0", - "@angular/core": "20.3.0" + "@angular/animations": "20.3.7", + "@angular/common": "20.3.7", + "@angular/core": "20.3.7" }, "peerDependenciesMeta": { "@angular/animations": { @@ -604,9 +582,9 @@ } }, "node_modules/@angular/router": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.0.tgz", - "integrity": "sha512-JshumajvPCMztz1+7r/l5tRxFL3cn2jCpr5szdc5hESkpytY4050hedd09GogL1UoIyZAjhyYLhSlMnvrgjHBA==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.7.tgz", + "integrity": "sha512-Lq7mCNcLP1npmNh2JlNEe02YS2jNnaLnCy/t//o+Qq0c6DGV78JRl7pHubiB2R6XXlgvOcZWg88v94Li+y85Iw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -615,9 +593,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.0", - "@angular/core": "20.3.0", - "@angular/platform-browser": "20.3.0", + "@angular/common": "20.3.7", + "@angular/core": "20.3.7", + "@angular/platform-browser": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -637,9 +615,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", "engines": { @@ -695,14 +673,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -817,9 +795,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -851,13 +829,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -882,18 +860,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -901,14 +879,14 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -924,40 +902,6 @@ "node": ">=0.1.90" } }, - "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", @@ -1400,68 +1344,10 @@ "node": ">=18" } }, - "node_modules/@fortawesome/angular-fontawesome": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-3.0.0.tgz", - "integrity": "sha512-+8Dd6DoJnqArfrZ5NvjHyRL64IIkTigXclbOOcFdYQ8/WFERQUDaEU6SAV8Q0JBpJhMS1McED7YCOCAE6SIVyA==", - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-svg-core": "^7.0.0", - "tslib": "^2.8.1" - }, - "peerDependencies": { - "@angular/core": "^20.0.0" - } - }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.0.1.tgz", - "integrity": "sha512-0VpNtO5cNe1/HQWMkl4OdncYK/mv9hnBte0Ew0n6DMzmo3Q3WzDFABHm6LeNTipt5zAyhQ6Ugjiu8aLaEjh1gg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.0.1.tgz", - "integrity": "sha512-x0cR55ILVqFpUioSMf6ebpRCMXMcheGN743P05W2RB5uCNpJUqWIqW66Lap8PfL/lngvjTbZj0BNSUweIr/fHQ==", - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-common-types": "7.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.0.1.tgz", - "integrity": "sha512-4V9fHbHjcx9Qu4O99AM5B4zuEDfB4zajk1I77hEzOxPN00f8g3484Aeq6WpfFcmookvjLE3Pr71Dhf/lqw7tbA==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "7.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.0.1.tgz", - "integrity": "sha512-esKuSrl1WMOTMDLNt38i16VfLe/gRZt2ZAJ3Yw7slfs7sj583MKqNFqO57zmhknk1Sya6f9Wys89aCzIJkcqlg==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "7.0.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@inquirer/ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz", - "integrity": "sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz", + "integrity": "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==", "dev": true, "license": "MIT", "engines": { @@ -1469,16 +1355,16 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.4.tgz", - "integrity": "sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.0.tgz", + "integrity": "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/core": "^10.2.2", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1516,15 +1402,15 @@ } }, "node_modules/@inquirer/core": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz", - "integrity": "sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.0.tgz", + "integrity": "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/ansi": "^1.0.1", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", @@ -1544,15 +1430,15 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.20", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.20.tgz", - "integrity": "sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==", + "version": "4.2.21", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.21.tgz", + "integrity": "sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", + "@inquirer/core": "^10.3.0", "@inquirer/external-editor": "^1.0.2", - "@inquirer/type": "^3.0.8" + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -1567,14 +1453,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.20", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.20.tgz", - "integrity": "sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.21.tgz", + "integrity": "sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1612,9 +1498,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", - "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", + "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", "dev": true, "license": "MIT", "engines": { @@ -1622,14 +1508,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.4.tgz", - "integrity": "sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.5.tgz", + "integrity": "sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -1644,14 +1530,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.20.tgz", - "integrity": "sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==", + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.21.tgz", + "integrity": "sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -1666,15 +1552,15 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.20", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.20.tgz", - "integrity": "sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.21.tgz", + "integrity": "sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8" + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -1719,14 +1605,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.8.tgz", - "integrity": "sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.9.tgz", + "integrity": "sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1742,15 +1628,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.3.tgz", - "integrity": "sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.0.tgz", + "integrity": "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1766,16 +1652,16 @@ } }, "node_modules/@inquirer/select": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.4.tgz", - "integrity": "sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.0.tgz", + "integrity": "sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/core": "^10.2.2", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -1791,9 +1677,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", - "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.9.tgz", + "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", "dev": true, "license": "MIT", "engines": { @@ -1954,13 +1840,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kurkle/color": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", - "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", - "license": "MIT", - "peer": true - }, "node_modules/@listr2/prompt-adapter-inquirer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.1.tgz", @@ -2531,19 +2410,6 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.5.tgz", - "integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", - "@tybys/wasm-util": "^0.10.1" - } - }, "node_modules/@npmcli/agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", @@ -2840,26 +2706,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@oxc-project/runtime": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.81.0.tgz", - "integrity": "sha512-zm/LDVOq9FEmHiuM8zO4DWirv0VP2Tv2VsgaiHby9nvpq+FVrcqNYgv+TysLKOITQXWZj/roluTxFvpkHP0Iuw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.81.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.81.0.tgz", - "integrity": "sha512-CnOqkybZK8z6Gx7Wb1qF7AEnSzbol1WwcIzxYOr8e91LytGOjo0wCpgoYWZo8sdbpqX+X+TJayIzo4Pv0R/KjA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", @@ -3203,216 +3049,10 @@ "node": ">=14" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.32.tgz", - "integrity": "sha512-Gs+313LfR4Ka3hvifdag9r44WrdKQaohya7ZXUXzARF7yx0atzFlVZjsvxtKAw1Vmtr4hB/RjUD1jf73SW7zDw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.32.tgz", - "integrity": "sha512-W8oMqzGcI7wKPXUtS3WJNXzbghHfNiuM1UBAGpVb+XlUCgYRQJd2PRGP7D3WGql3rR3QEhUvSyAuCBAftPQw6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.32.tgz", - "integrity": "sha512-pM4c4sKUk37noJrnnDkJknLhCsfZu7aWyfe67bD0GQHfzAPjV16wPeD9CmQg4/0vv+5IfHYaa4VE536xbA+W0Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.32.tgz", - "integrity": "sha512-M8SUgFlYb5kJJWcFC8gUMRiX4WLFxPKMed3SJ2YrxontgIrEcpizPU8nLNVsRYEStoSfKHKExpQw3OP6fm+5bw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.32.tgz", - "integrity": "sha512-FuQpbNC/hE//bvv29PFnk0AtpJzdPdYl5CMhlWPovd9g3Kc3lw9TrEPIbL7gRPUdhKAiq6rVaaGvOnXxsa0eww==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.32.tgz", - "integrity": "sha512-hRZygRlaGCjcNTNY9GV7dDI18sG1dK3cc7ujHq72LoDad23zFDUGMQjiSxHWK+/r92iMV+j2MiHbvzayxqynsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.32.tgz", - "integrity": "sha512-HzgT6h+CXLs+GKAU0Wvkt3rvcv0CmDBsDjlPhh4GHysOKbG9NjpKYX2zvjx671E9pGbTvcPpwy7gGsy7xpu+8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.32.tgz", - "integrity": "sha512-Ab/wbf6gdzphDbsg51UaxsC93foQ7wxhtg0SVCXd25BrV4MAJ1HoDtKN/f4h0maFmJobkqYub2DlmoasUzkvBg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.32.tgz", - "integrity": "sha512-VoxqGEfh5A1Yx+zBp/FR5QwAbtzbuvky2SVc+ii4g1gLD4zww6mt/hPi5zG+b88zYPFBKHpxMtsz9cWqXU5V5Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.32.tgz", - "integrity": "sha512-qZ1ViyOUDGbiZrSAJ/FIAhYUElDfVxxFW6DLT/w4KeoZN3HsF4jmRP95mXtl51/oGrqzU9l9Q2f7/P4O/o2ZZA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.32.tgz", - "integrity": "sha512-hEkG3wD+f3wytV0lqwb/uCrXc4r4Ny/DWJFJPfQR3VeMWplhWGgSHNwZc2Q7k86Yi36f9NNzzWmrIuvHI9lCVw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.32.tgz", - "integrity": "sha512-k3MvDf8SiA7uP2ikP0unNouJ2YCrnwi7xcVW+RDgMp5YXVr3Xu6svmT3HGn0tkCKUuPmf+uy8I5uiHt5qWQbew==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rolldown/binding-win32-ia32-msvc": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.32.tgz", - "integrity": "sha512-wAi/FxGh7arDOUG45UmnXE1sZUa0hY4cXAO2qWAjFa3f7bTgz/BqwJ7XN5SUezvAJPNkME4fEpInfnBvM25a0w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.32.tgz", - "integrity": "sha512-Ej0i4PZk8ltblZtzVK8ouaGUacUtxRmTm5S9794mdyU/tYxXjAJNseOfxrnHpMWKjMDrOKbqkPqJ52T9NR4LQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz", - "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==", - "dev": true, - "license": "MIT" - }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", - "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", "cpu": [ "arm" ], @@ -3424,9 +3064,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", - "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", "cpu": [ "arm64" ], @@ -3438,9 +3078,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", - "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", "cpu": [ "arm64" ], @@ -3452,9 +3092,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", - "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", "cpu": [ "x64" ], @@ -3466,9 +3106,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", - "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", "cpu": [ "arm64" ], @@ -3480,9 +3120,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", - "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", "cpu": [ "x64" ], @@ -3494,9 +3134,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", - "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", "cpu": [ "arm" ], @@ -3508,9 +3148,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", - "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", "cpu": [ "arm" ], @@ -3522,9 +3162,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", - "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", "cpu": [ "arm64" ], @@ -3536,9 +3176,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", - "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", "cpu": [ "arm64" ], @@ -3550,9 +3190,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", - "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", "cpu": [ "loong64" ], @@ -3564,9 +3204,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", - "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", "cpu": [ "ppc64" ], @@ -3578,9 +3218,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", - "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", "cpu": [ "riscv64" ], @@ -3592,9 +3232,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", - "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", "cpu": [ "riscv64" ], @@ -3606,9 +3246,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", - "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", "cpu": [ "s390x" ], @@ -3620,9 +3260,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", - "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", "cpu": [ "x64" ], @@ -3634,9 +3274,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", - "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", "cpu": [ "x64" ], @@ -3648,9 +3288,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", - "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", "cpu": [ "arm64" ], @@ -3662,9 +3302,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", - "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", "cpu": [ "arm64" ], @@ -3676,9 +3316,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", - "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", "cpu": [ "ia32" ], @@ -3689,10 +3329,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", - "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", "cpu": [ "x64" ], @@ -3704,14 +3358,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.1.tgz", - "integrity": "sha512-v2SNPaEHuMZyL85tYEQeFJvf7cFxSzXHbotcCrXRBuK3RSAvYXxWlpuBU+jGfZq2FjFZ+G7nHJZLAA/a1UqAvA==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.7.tgz", + "integrity": "sha512-jR2LPJVGK6yzPTNXkGJZYtdeLGkNdqJhVow2E+ILt3pk/LZuT/iSdr9V4nArU9yysifGuJFTyZapVOYkEYaykg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.1", - "@angular-devkit/schematics": "20.3.1", + "@angular-devkit/core": "20.3.7", + "@angular-devkit/schematics": "20.3.7", "jsonc-parser": "3.3.1" }, "engines": { @@ -3841,9 +3495,9 @@ "license": "Apache-2.0" }, "node_modules/@stomp/stompjs": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.2.0.tgz", - "integrity": "sha512-+Waf/wdSxgRmXHWg7HXUt51YNK/Cp2pMhW+OELwX5fzbO6pd+O2bLrWqGmyFNC/RyeUQIMGESP7oE3L1dXXm0Q==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.2.1.tgz", + "integrity": "sha512-DLd/WeicnHS5SsWWSk3x6/pcivqchNaEvg9UEGVqAcfYEBVmS9D6980ckXjTtfpXLjdLDsd96M7IuX4w7nzq5g==", "license": "Apache-2.0" }, "node_modules/@tufjs/canonical-json": { @@ -3896,17 +3550,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/cors": { "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", @@ -3925,20 +3568,20 @@ "license": "MIT" }, "node_modules/@types/jasmine": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.9.tgz", - "integrity": "sha512-8t4HtkW4wxiPVedMpeZ63n3vlWxEIquo/zc1Tm8ElU+SqVV7+D3Na2PWaJUp179AzTragMWVwkMv7mvty0NfyQ==", + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.12.tgz", + "integrity": "sha512-1BzPxNsFDLDfj9InVR3IeY0ZVf4o9XV+4mDqoCfyPkbsA7dYyKAPAb2co6wLFlHcvxPlt1wShm7zQdV7uTfLGA==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.0.tgz", - "integrity": "sha512-y1dMvuvJspJiPSDZUQ+WMBvF7dpnEqN4x9DDC9ie5Fs/HUZJA3wFp7EhHoVaKX/iI0cRoECV8X2jL8zi0xrHCg==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", + "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.12.0" + "undici-types": "~7.16.0" } }, "node_modules/@vitejs/plugin-basic-ssl": { @@ -4063,9 +3706,9 @@ "license": "MIT" }, "node_modules/ansi-escapes": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.0.tgz", - "integrity": "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.1.tgz", + "integrity": "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4104,16 +3747,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansis": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", - "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4159,9 +3792,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.4.tgz", - "integrity": "sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==", + "version": "2.8.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz", + "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4267,9 +3900,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.0.tgz", - "integrity": "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", "dev": true, "funding": [ { @@ -4287,11 +3920,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.2", - "caniuse-lite": "^1.0.30001741", - "electron-to-chromium": "^1.5.218", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -4405,34 +4038,17 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacache/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", "dev": true, "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -4481,9 +4097,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001741", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", - "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", "dev": true, "funding": [ { @@ -4521,29 +4137,6 @@ "dev": true, "license": "MIT" }, - "node_modules/chart.js": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", - "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, - "node_modules/chartjs-adapter-date-fns": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz", - "integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==", - "license": "MIT", - "peerDependencies": { - "chart.js": ">=2.8.0", - "date-fns": ">=2.0.0" - } - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -4907,16 +4500,6 @@ "dev": true, "license": "MIT" }, - "node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, "node_modules/date-format": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", @@ -4967,9 +4550,9 @@ } }, "node_modules/detect-libc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", - "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5086,16 +4669,16 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.218", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz", - "integrity": "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==", + "version": "1.5.241", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.241.tgz", + "integrity": "sha512-ILMvKX/ZV5WIJzzdtuHg8xquk2y0BOGlFOxBVwTpbiXqWIH0hamG45ddU4R3PQ0gYu+xgo0vdHXHli9sHIGb4w==", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, @@ -5436,9 +5019,9 @@ } }, "node_modules/exponential-backoff": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", - "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "dev": true, "license": "Apache-2.0" }, @@ -5900,9 +5483,9 @@ } }, "node_modules/hosted-git-info": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.0.tgz", - "integrity": "sha512-gEf705MZLrDPkbbhi8PnoO4ZwYgKoNL+ISZ3AjZMht2r3N5tuTwncyDi6Fv2/qDnMmZxgs0yI8WDOyR8q3G+SQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "dev": true, "license": "ISC", "dependencies": { @@ -5913,9 +5496,9 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", - "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", "dev": true, "license": "ISC", "engines": { @@ -6100,9 +5683,9 @@ } }, "node_modules/immutable": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", - "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", "dev": true, "license": "MIT" }, @@ -6968,9 +6551,9 @@ } }, "node_modules/less": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/less/-/less-4.4.1.tgz", - "integrity": "sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/less/-/less-4.4.2.tgz", + "integrity": "sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7123,12 +6706,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, "node_modules/log-symbols": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", @@ -7581,9 +7158,9 @@ "license": "ISC" }, "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -7728,24 +7305,6 @@ "node": ">= 0.6" } }, - "node_modules/ng2-charts": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-8.0.0.tgz", - "integrity": "sha512-nofsNHI2Zt+EAwT+BJBVg0kgOhNo9ukO4CxULlaIi7VwZSr7I1km38kWSoU41Oq6os6qqIh5srnL+CcV+RFPFA==", - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.15", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/cdk": ">=19.0.0", - "@angular/common": ">=19.0.0", - "@angular/core": ">=19.0.0", - "@angular/platform-browser": ">=19.0.0", - "chart.js": "^3.4.0 || ^4.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, "node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", @@ -7755,9 +7314,9 @@ "optional": true }, "node_modules/node-gyp": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.4.2.tgz", - "integrity": "sha512-3gD+6zsrLQH7DyYOUIutaauuXrcyxeTPyQuZQCQoNPZMHMMS5m4y0xclNpvYzoK3VNzuyxT6eF4mkIL4WSZ1eQ==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", + "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7815,34 +7374,17 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/node-gyp/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", "dev": true, "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -7876,9 +7418,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", + "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", "dev": true, "license": "MIT" }, @@ -7961,18 +7503,29 @@ } }, "node_modules/npm-packlist": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.1.tgz", - "integrity": "sha512-vaC03b2PqJA6QqmwHi1jNU8fAPXEnnyv4j/W4PVfgm24C4/zZGSVut3z0YUeN0WIFCo1oGOL02+6LbvFK7JL4Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", "dev": true, "license": "ISC", "dependencies": { - "ignore-walk": "^8.0.0" + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", + "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/npm-pick-manifest": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", @@ -8290,6 +7843,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -8343,6 +7897,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -8738,42 +8293,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rolldown": { - "version": "1.0.0-beta.32", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.32.tgz", - "integrity": "sha512-vxI2sPN07MMaoYKlFrVva5qZ1Y7DAZkgp7MQwTnyHt4FUMz9Sh+YeCzNFV9JYHI6ZNwoGWLCfCViE3XVsRC1cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/runtime": "=0.81.0", - "@oxc-project/types": "=0.81.0", - "@rolldown/pluginutils": "1.0.0-beta.32", - "ansis": "^4.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-beta.32", - "@rolldown/binding-darwin-arm64": "1.0.0-beta.32", - "@rolldown/binding-darwin-x64": "1.0.0-beta.32", - "@rolldown/binding-freebsd-x64": "1.0.0-beta.32", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.32", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.32", - "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.32", - "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.32", - "@rolldown/binding-linux-x64-musl": "1.0.0-beta.32", - "@rolldown/binding-openharmony-arm64": "1.0.0-beta.32", - "@rolldown/binding-wasm32-wasi": "1.0.0-beta.32", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.32", - "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.32", - "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.32" - } - }, "node_modules/rollup": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", - "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", "dev": true, "license": "MIT", "dependencies": { @@ -8787,27 +8310,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.2", - "@rollup/rollup-android-arm64": "4.50.2", - "@rollup/rollup-darwin-arm64": "4.50.2", - "@rollup/rollup-darwin-x64": "4.50.2", - "@rollup/rollup-freebsd-arm64": "4.50.2", - "@rollup/rollup-freebsd-x64": "4.50.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", - "@rollup/rollup-linux-arm-musleabihf": "4.50.2", - "@rollup/rollup-linux-arm64-gnu": "4.50.2", - "@rollup/rollup-linux-arm64-musl": "4.50.2", - "@rollup/rollup-linux-loong64-gnu": "4.50.2", - "@rollup/rollup-linux-ppc64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-musl": "4.50.2", - "@rollup/rollup-linux-s390x-gnu": "4.50.2", - "@rollup/rollup-linux-x64-gnu": "4.50.2", - "@rollup/rollup-linux-x64-musl": "4.50.2", - "@rollup/rollup-openharmony-arm64": "4.50.2", - "@rollup/rollup-win32-arm64-msvc": "4.50.2", - "@rollup/rollup-win32-ia32-msvc": "4.50.2", - "@rollup/rollup-win32-x64-msvc": "4.50.2", + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" } }, @@ -9760,9 +9284,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -9801,9 +9325,9 @@ } }, "node_modules/undici-types": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", - "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, @@ -9854,9 +9378,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, "funding": [ { @@ -9946,9 +9470,9 @@ } }, "node_modules/vite": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", - "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/main/angular/package.json b/src/main/angular/package.json index 697b121..402153e 100644 --- a/src/main/angular/package.json +++ b/src/main/angular/package.json @@ -28,21 +28,15 @@ "@angular/forms": "^20.3.0", "@angular/platform-browser": "^20.3.0", "@angular/router": "^20.3.0", - "@fortawesome/angular-fontawesome": "^3.0.0", - "@fortawesome/free-regular-svg-icons": "^7.0.0", - "@fortawesome/free-solid-svg-icons": "^7.0.0", - "@stomp/ng2-stompjs": "^8.0.0", - "@stomp/stompjs": "^7.2.0", - "chartjs-adapter-date-fns": "^3.0.0", - "date-fns": "^4.1.0", - "ng2-charts": "^8.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", - "zone.js": "~0.15.0" + "zone.js": "~0.15.0", + "@stomp/ng2-stompjs": "^8.0.0", + "@stomp/stompjs": "^7.2.1" }, "devDependencies": { - "@angular/build": "^20.3.1", - "@angular/cli": "^20.3.1", + "@angular/build": "^20.3.7", + "@angular/cli": "^20.3.7", "@angular/compiler-cli": "^20.3.0", "@types/jasmine": "~5.1.0", "jasmine-core": "~5.9.0", diff --git a/src/main/angular/src/app/COMMON.ts b/src/main/angular/src/app/COMMON.ts deleted file mode 100644 index a076f76..0000000 --- a/src/main/angular/src/app/COMMON.ts +++ /dev/null @@ -1,350 +0,0 @@ -import {StompService} from "@stomp/ng2-stompjs"; -import {filter, map, Subject, Subscription} from "rxjs"; -import {HttpClient} from '@angular/common/http'; -import {Injectable} from '@angular/core'; -import {RxStompState} from '@stomp/rx-stomp'; -import {formatNumber} from '@angular/common'; - -export type FromJson = (json: any) => T; - -export type FromJsonIndexed = (json: any, index: number) => T; - -export type Next = (item: T) => any; - -export type Compare = (a: T, b: T) => number; - -export type Equals = (a: T, b: T) => boolean; - -export function stompServiceFactory() { - const stomp = new StompService({ - url: url('ws', ['websocket']), - debug: false, - heartbeat_in: 2000, - heartbeat_out: 2000, - reconnect_delay: 2000, - headers: {}, - }); - stomp.activate(); - return stomp; -} - -export function validateNumber(json: any): number { - if (typeof json !== 'number') { - throw new Error("Not a number: " + JSON.stringify(json)); - } - return json; -} - -export function validateString(json: any): string { - if (typeof json !== 'string') { - throw new Error("Not a string: " + JSON.stringify(json)); - } - return json; -} - -export function validateDate(json: any): Date { - return new Date(Date.parse(validateString(json))); -} - -export function validateBoolean(json: any): boolean { - if (typeof json !== 'boolean') { - throw new Error("Not a boolean: " + JSON.stringify(json)); - } - return json; -} - -export function validateEnum>(value: any, enumType: T): T[keyof T] { - const str = validateString(value); - if (Object.values(enumType).includes(str)) { - return str as T[keyof T]; - } - throw new Error(`Invalid enum value: ${str}`); -} - -export function validateList(json: any, fromJson: FromJson): T[] { - return json.map(fromJson); -} - -export function validateListIndexed(json: any, fromJson: FromJsonIndexed): T[] { - return json.map(fromJson); -} - -export function url(protocol: string, path: any[]): string { - const secure = location.protocol.endsWith('s:') ? 's' : ''; - return `${protocol}${secure}://${location.hostname}:8080/${path.join('/')}`; -} - -export const compareDates = (a: Date, b: Date) => a.getTime() - b.getTime(); - -export const compareStrings = (a: string, b: string) => a.localeCompare(b); - -export const compareNumbers = (a: number, b: number) => a - b; - -export const compareBool = (a: boolean, b: boolean) => (a ? 1 : 0) - (b ? 1 : 0); - -export function compareNullable(compare: Compare): Compare { - return (a: T | null | undefined, b: T | null | undefined) => { - const aNull = a === null || a === undefined; - const bNull = b === null || b === undefined; - if (aNull) { - if (bNull) { - return 0; - } else { - return +1; - } - } else { - if (bNull) { - return -1; - } else { - return compare(a, b); - } - } - }; -} - -export function mapNotNull(t: T | null | undefined, mapper: (t: T) => R): R | null { - if (t === null || t === undefined) { - return null; - } - return mapper(t); -} - -export function mapNotNullOrGet(t: T | null | undefined, mapper: (t: T) => R, getFallback: () => R): R { - if (t === null || t === undefined) { - return getFallback(); - } - return mapper(t); -} - -@Injectable({ - providedIn: 'root' -}) -export class ApiService { - - constructor( - private readonly http: HttpClient, - private readonly stompService: StompService, - ) { - this.websocketConnected(() => this._websocketError = false); - this.websocketDisconnected(() => this._websocketError = true); - } - - get websocketError(): boolean { - return this._websocketError; - } - - getSingle(path: any[], fromJson: FromJson, next?: Next): void { - this.http.get(url('http', path)).pipe(map(fromJson)).subscribe(next); - } - - getList(path: any[], fromJson: FromJson, next?: Next): void { - this.http.get(url('http', path)).pipe(map(list => list.map(fromJson))).subscribe(next); - } - - postSingle(path: any[], data: any, fromJson: FromJson, next?: Next): void { - this.http.post(url('http', path), data).pipe(map(fromJson)).subscribe(next); - } - - postList(path: any[], data: any, fromJson: FromJson, next?: Next): void { - this.http.post(url('http', path), data).pipe(map(list => list.map(fromJson))).subscribe(next); - } - - private _websocketError: boolean = false; - - websocketConnected(next: Next): Subscription { - return this.stompService.connectionState$.pipe(filter(state => state === RxStompState.OPEN)).subscribe(_ => next()); - } - - websocketDisconnected(next: Next): Subscription { - return this.stompService.connectionState$.pipe(filter(state => state !== RxStompState.OPEN)).subscribe(_ => next()); - } - - subscribe(topic: any[], fromJson: FromJson, next?: Next): Subscription { - return this.stompService - .subscribe(topic.join("/")) - .pipe( - map(message => message.body), - map(b => JSON.parse(b)), - map(j => fromJson(j)), - ) - .subscribe(next); - } - -} - -export abstract class CrudService { - - protected constructor( - readonly api: ApiService, - readonly path: any[], - readonly fromJson: FromJson, - ) { - // - } - - findAll(next: Next): void { - this.getList(["findAll"], next); - } - - protected getSingle(path: any[], next?: Next) { - this.api.getSingle([...this.path, ...path], this.fromJson, next); - } - - protected getList(path: any[], next?: Next) { - this.api.getList([...this.path, ...path], this.fromJson, next); - } - - protected postSingle(path: any[], data: any, next?: Next) { - this.api.postSingle([...this.path, ...path], data, this.fromJson, next); - } - - subscribe(next: Next, path: any[] = []): Subscription { - const subs: Subscription[] = []; - subs.push(this.api.subscribe([...this.path, ...path], this.fromJson, next)); - return new Subscription(() => subs.forEach(sub => sub.unsubscribe())); - } - -} - -export abstract class ID> { - - abstract get id(): number; - - abstract get deleted(): boolean; - - hasId(id: number): boolean { - return this.id === id; - } - - equals(t: T | null): boolean { - return t !== null && t !== undefined && t.hasId(this.id); - } - - equals_(): (t: (T | null)) => boolean { - return (t: T | null) => this.equals(t); - } - - static equals>(a: X, b: X): boolean { - return ID.hasId(a.id)(b); - } - - static hasId>(id: number): (t: X) => boolean { - return (t: X) => t.hasId(id); - } - -} - -export abstract class EntityListService> extends CrudService { - - private readonly subject: Subject = new Subject(); - - private _list: T[] = []; - - protected constructor( - api: ApiService, - path: any[], - fromJson: FromJson, - readonly equals: Equals, - readonly compare: Compare, - ) { - super(api, path, fromJson); - this.findAll(all => { - this._list = all; - all.forEach(t => this.subject.next(t)); - }); - this.subscribe(this._update); - } - - private readonly _update = (fresh: T) => { - const index = this._list.findIndex(e => this.equals(e, fresh)); - if (fresh.deleted) { - if (index >= 0) { - this._list.splice(index, 1); - } - } else if (index >= 0) { - this._list[index] = fresh; - } else { - this._list.push(fresh); - } - this._list = this._list.sort(this.compare); - this.subject.next(fresh); - }; - - get list(): T[] { - return [...this._list]; - } - - subscribeListItems(next: Next): Subscription { - return this.subject.subscribe(next); - } - - byId(id: number): T { - return this._list.filter(t => t.hasId(id))[0]; - } - -} - -export function NTU(v: T | null | undefined): T | undefined { - if (v === null) { - return undefined; - } - return v; -} - -export function UTN(v: T | null | undefined): T | null { - if (v === undefined) { - return null; - } - return v; -} - -export function ageString(event: Date | null, now: Date, long: boolean = true) { - if (event === null) { - return '-'; - } - const secondsTotal = Math.max(0, (now.getTime() - event.getTime()) / 1000); - - const minutesTotal = secondsTotal / 60; - const hoursTotal = minutesTotal / 60; - const daysTotal = hoursTotal / 24; - const yearsTotal = daysTotal / 365; - - const secondsPart = secondsTotal % 60; - const minutesPart = minutesTotal % 60; - const hoursPart = hoursTotal % 24; - const daysPart = daysTotal % 365; - - const locale = 'de-DE'; - const longDigits = "2.0-0"; - const shortDigits = "0.0-0"; - if (yearsTotal >= 1) { - if (long) { - return `${formatNumber(yearsTotal, locale, shortDigits)}y ${formatNumber(daysPart, locale, longDigits)}d ${formatNumber(hoursPart, locale, longDigits)}h ${formatNumber(minutesPart, locale, longDigits)}m ${formatNumber(secondsPart, locale, longDigits)}s`; - } else { - return `${formatNumber(yearsTotal, locale, shortDigits)}y ${formatNumber(daysPart, locale, shortDigits)}d`; - } - } - if (daysTotal >= 1) { - if (long) { - return `${formatNumber(daysPart, locale, shortDigits)}d ${formatNumber(hoursPart, locale, longDigits)}h ${formatNumber(minutesPart, locale, longDigits)}m ${formatNumber(secondsPart, locale, longDigits)}s`; - } else { - return `${formatNumber(daysPart, locale, shortDigits)}d ${formatNumber(hoursPart, locale, shortDigits)}h`; - } - } - if (hoursTotal >= 1) { - if (long) { - return `${formatNumber(hoursPart, locale, shortDigits)}h ${formatNumber(minutesPart, locale, longDigits)}m ${formatNumber(secondsPart, locale, longDigits)}s`; - } else { - return `${formatNumber(hoursPart, locale, shortDigits)}h ${formatNumber(minutesPart, locale, shortDigits)}m`; - } - } - if (minutesTotal >= 1) { - if (long) { - return `${formatNumber(minutesPart, locale, shortDigits)}m ${formatNumber(secondsPart, locale, longDigits)}s`; - } else { - return `${formatNumber(minutesPart, locale, shortDigits)}m ${formatNumber(secondsPart, locale, shortDigits)}s`; - } - } - return `${formatNumber(secondsPart, locale, shortDigits)}s`; -} - diff --git a/src/main/angular/src/app/app.config.ts b/src/main/angular/src/app/app.config.ts index 4ac6c4b..63643d0 100644 --- a/src/main/angular/src/app/app.config.ts +++ b/src/main/angular/src/app/app.config.ts @@ -1,15 +1,14 @@ -import {ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection} from '@angular/core'; +import {ApplicationConfig, LOCALE_ID, provideBrowserGlobalErrorListeners, provideZoneChangeDetection} from '@angular/core'; import {provideRouter} from '@angular/router'; -import 'chartjs-adapter-date-fns'; - import {routes} from './app.routes'; import {provideHttpClient} from '@angular/common/http'; -import {stompServiceFactory} from './COMMON'; + import {StompService} from '@stomp/ng2-stompjs'; import {registerLocaleData} from '@angular/common'; import localeDe from '@angular/common/locales/de'; import localeDeExtra from '@angular/common/locales/extra/de'; +import {stompServiceFactory} from './common'; registerLocaleData(localeDe, 'de-DE', localeDeExtra); @@ -20,5 +19,6 @@ export const appConfig: ApplicationConfig = { provideRouter(routes), provideHttpClient(), {provide: StompService, useFactory: stompServiceFactory}, + {provide: LOCALE_ID, useValue: 'de-DE'}, ] }; diff --git a/src/main/angular/src/app/app.html b/src/main/angular/src/app/app.html index 59202e0..7dd570e 100644 --- a/src/main/angular/src/app/app.html +++ b/src/main/angular/src/app/app.html @@ -1,21 +1 @@ - - -
- -
+ diff --git a/src/main/angular/src/app/app.less b/src/main/angular/src/app/app.less index 21bce2f..e69de29 100644 --- a/src/main/angular/src/app/app.less +++ b/src/main/angular/src/app/app.less @@ -1,54 +0,0 @@ -@sidebarColor: #62b0ca; - -.sidebar { - position: fixed; - display: flex; - height: 100%; - pointer-events: none; - user-select: none; - - > * { - pointer-events: all; - } - - .handle { - padding: 0.5em; - height: 1.5em; - border-right: 1px solid gray; - border-bottom: 1px solid gray; - border-bottom-right-radius: 0.5em; - background-color: @sidebarColor; - font-weight: bold; - } - - .handle:hover { - background-color: lightyellow; - } - - .content { - height: 100%; - border-right: 1px solid gray; - background-color: @sidebarColor; - width: 300px; - max-width: calc(100vw - 1em); - - .item { - padding: 0.5em; - } - - .item:hover { - background-color: lightyellow; - } - - .itemActive { - background-color: steelblue; - font-weight: bold; - } - - } - -} - -.bodyContent { - margin-left: 2em; -} diff --git a/src/main/angular/src/app/app.routes.ts b/src/main/angular/src/app/app.routes.ts index 55f36d7..03015a5 100644 --- a/src/main/angular/src/app/app.routes.ts +++ b/src/main/angular/src/app/app.routes.ts @@ -1,14 +1,7 @@ import {Routes} from '@angular/router'; -import {PlotEditor} from './plot/editor/plot-editor.component'; -import {DashboardComponent} from './dashboard/dashboard.component'; -import {TopicListComponent} from './topic/list/topic-list.component'; -import {SeriesListComponent} from './series/list/series-list.component'; +import {LocationList} from './location/list/location-list'; export const routes: Routes = [ - {path: 'Dashboard', component: DashboardComponent}, - {path: 'TopicList', component: TopicListComponent}, - {path: 'SeriesList', component: SeriesListComponent}, - {path: 'PlotEditor', component: PlotEditor}, - {path: 'PlotEditor/:id', component: PlotEditor}, - {path: '**', redirectTo: 'Dashboard'}, + {path: 'LocationList', component: LocationList}, + {path: '**', redirectTo: 'LocationList'} ]; diff --git a/src/main/angular/src/app/app.ts b/src/main/angular/src/app/app.ts index 48224af..0f4e198 100644 --- a/src/main/angular/src/app/app.ts +++ b/src/main/angular/src/app/app.ts @@ -1,14 +1,11 @@ import {Component} from '@angular/core'; -import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router'; +import {RouterOutlet} from '@angular/router'; @Component({ selector: 'app-root', - imports: [RouterOutlet, RouterLink, RouterLinkActive], + imports: [RouterOutlet], templateUrl: './app.html', styleUrl: './app.less' }) export class App { - - protected sidebar: boolean = false; - } diff --git a/src/main/angular/src/app/common.ts b/src/main/angular/src/app/common.ts new file mode 100644 index 0000000..ea48b48 --- /dev/null +++ b/src/main/angular/src/app/common.ts @@ -0,0 +1,451 @@ +import {Inject, Injectable, LOCALE_ID} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; +import {filter, map, Subscription} from 'rxjs'; +import {StompService} from '@stomp/ng2-stompjs'; +import {RxStompState} from '@stomp/rx-stomp'; +import {formatNumber} from '@angular/common'; + +export type FromJson = (json: any) => T; + +export type Next = (t: T) => any; + +export function validateBoolean(value: any): boolean { + if (typeof value !== 'boolean') { + throw new Error(`Not a boolean: ${JSON.stringify(value)}`); + } + return value; +} + +export function validateNumber(value: any): number { + if (typeof value !== 'number') { + throw new Error(`Not a number: ${JSON.stringify(value)}`); + } + return value; +} + +export function validateString(value: any): string { + if (typeof value !== 'string') { + throw new Error(`Not a string: ${JSON.stringify(value)}`); + } + return value; +} + +export function validateDate(value: any): Date { + return new Date(Date.parse(validateString(value))); +} + +export function validateList(value: any, fromJson: FromJson): T[] { + return value.map(fromJson); +} + +export function validateEnum>(value: any, enumType: T): T[keyof T] { + const str = validateString(value); + if (Object.values(enumType).includes(str)) { + return str as T[keyof T]; + } + throw new Error(`Invalid enum value: ${str}`); +} + +export function or(t: T | null | undefined, map: (t: T) => R, orElse: E): R | E { + return t === null || t === undefined ? orElse : map(t); +} + +export function url(protocol: string, path: any[]): string { + return `${protocol}${location.protocol === 'https://' ? 's' : ''}://localhost:8080/${path.join('/')}`; +} + +export function stompServiceFactory() { + const stomp = new StompService({ + url: url('ws', ['websocket']), + debug: false, + heartbeat_in: 2000, + heartbeat_out: 2000, + reconnect_delay: 2000, + headers: {}, + }); + stomp.activate(); + return stomp; +} + +@Injectable({ + providedIn: 'root' +}) +export class WebsocketService { + constructor( + readonly stompService: StompService, + ) { + this.websocketConnected(() => this._websocketError = false); + this.websocketDisconnected(() => this._websocketError = true); + } + + private _websocketError: boolean = false; + + get websocketError(): boolean { + return this._websocketError; + } + + websocketConnected(next: Next): Subscription { + return this.stompService.connectionState$.pipe(filter(state => state === RxStompState.OPEN)).subscribe(_ => next()); + } + + websocketDisconnected(next: Next): Subscription { + return this.stompService.connectionState$.pipe(filter(state => state !== RxStompState.OPEN)).subscribe(_ => next()); + } + + subscribe(topic: any[], fromJson: FromJson, next: Next): Subscription { + return this.stompService + .subscribe(topic.join("/")) + .pipe( + map(message => message.body), + map(b => JSON.parse(b)), + map(j => fromJson(j)), + ) + .subscribe(next); + } + +} + +@Injectable({ + providedIn: 'root' +}) +export class Locale { + + private static LOCALE: string = 'de-DE'; + + static get value(): string { + return Locale.LOCALE; + } + + constructor( + @Inject(LOCALE_ID) readonly locale: string, + ) { + Locale.LOCALE = locale; + } + +} + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + + constructor( + readonly client: HttpClient, + ) { + } + + getNone(path: any[], fromJson: FromJson, next?: Next): void { + this.client.get(url('http', path)).subscribe(next); + } + + getSingle(path: any[], fromJson: FromJson, next?: Next): void { + this.client.get(url('http', path)).pipe(map(fromJson)).subscribe(next); + } + + getList(path: any[], fromJson: FromJson, next?: Next): void { + this.client.get(url('http', path)).pipe(map(list => list.map(fromJson))).subscribe(next); + } + + postNone(path: any[], data: any, fromJson: FromJson, next?: Next): void { + this.client.post(url('http', path), data).subscribe(next); + } + + postSingle(path: any[], data: any, fromJson: FromJson, next?: Next): void { + this.client.post(url('http', path), data).pipe(map(fromJson)).subscribe(next); + } + + postList(path: any[], data: any, fromJson: FromJson, next?: Next): void { + this.client.post(url('http', path), data).pipe(map(list => list.map(fromJson))).subscribe(next); + } + + postPage(path: any[], data: any, fromJson: FromJson, next?: Next>): void { + this.client.post(url('http', path), data).pipe(map(Page.fromJson(fromJson))).subscribe(next); + } + +} + +export class Page { + + static readonly EMPTY = new Page([]); + + constructor( + readonly content: T[], + ) { + // + } + + static fromJson(fromJson: FromJson): FromJson> { + return (json: any) => new Page( + validateList(json.content, fromJson), + ); + } + +} + +export abstract class CrudService { + + protected constructor( + readonly api: ApiService, + readonly ws: WebsocketService, + readonly path: any[], + readonly fromJson: FromJson, + ) { + // + } + + protected getNone(path: any[], next?: Next): void { + this.api.getNone([...this.path, ...path], this.fromJson, next); + } + + protected getSingle(path: any[], next?: Next): void { + this.api.getSingle([...this.path, ...path], this.fromJson, next); + } + + protected getList(path: any[], next?: Next): void { + this.api.getList([...this.path, ...path], this.fromJson, next); + } + + protected postNone(path: any[], data: any, next?: Next): void { + this.api.postNone([...this.path, ...path], data, this.fromJson, next); + } + + protected postSingle(path: any[], data: any, next?: Next): void { + this.api.postSingle([...this.path, ...path], data, this.fromJson, next); + } + + protected postList(path: any[], data: any, next?: Next): void { + this.api.postList([...this.path, ...path], data, this.fromJson, next); + } + + protected postPage(path: any[], data: any, next?: Next>): void { + this.api.postPage([...this.path, ...path], data, this.fromJson, next); + } + + subscribe(next: Next): Subscription { + return this.ws.subscribe([...this.path], this.fromJson, next); + } + +} + +export enum SortDirection { + ASC = 'ASC', + DESC = 'DESC' +} + +export class SortOrder { + + constructor( + readonly property: string, + readonly direction: SortDirection, + ) { + // + } + +} + +export class PageFilter { + + page: number = 0; + + size: number = 20; + + orders: SortOrder[] = []; + +} + +export type ID = { + id: number; +}; + +export class Duration { + + static readonly ZERO = new Duration(0); + + readonly seconds: number; + + readonly minutes: number; + + readonly hours: number; + + readonly days: number; + + readonly weeks: number; + + readonly months: number; + + readonly years: number; + + constructor( + readonly milliseconds: number, + ) { + this.seconds = milliseconds / 1000; + this.minutes = this.seconds / 60; + this.hours = this.minutes / 60; + this.days = this.hours / 24; + this.weeks = this.days / 7; + this.months = this.days / 30; + this.years = this.days / 365; + } + + get short(): string { + return `${formatNumber(this.minutes, Locale.value, '0.0-0')} min`; + } + + static ofMillis(millis: number) { + return new Duration(millis); + } + + static ofSeconds(seconds: number) { + return this.ofMillis(seconds * 1000); + } + + static ofMinutes(minutes: number) { + return this.ofSeconds(minutes * 60); + } + + static ofHours(hours: number) { + return this.ofMinutes(hours * 60); + } + + static ofDays(days: number): Duration { + return this.ofHours(days * 24); + } + + static ofWeeks(weeks: number): Duration { + return this.ofDays(weeks * 7); + } + + static ofMonths(months: number): Duration { + return this.ofDays(months * 30); + } + + static ofYears(years: number): Duration { + return this.ofDays(years * 365); + } + + static fromJson(json: any): Duration { + return new Duration(validateNumber(json),); + } + + lessThan(other: Duration) { + return this.milliseconds < other.milliseconds; + } + + times(factor: number): Duration { + return new Duration(this.milliseconds * factor); + } + + addTo(date: Date): Date { + return new Date(date.getTime() + this.milliseconds); + } + + subtractFrom(date: Date): Date { + return new Date(date.getTime() - this.milliseconds); + } + + plus(duration: Duration) { + return new Duration(this.milliseconds + duration.milliseconds); + } + + isZero() { + return this.milliseconds === 0; + } + + divideBy(duration: Duration): number { + return this.milliseconds / duration.milliseconds; + } + + static between(begin: Date, end: Date): Duration { + return new Duration(end.getTime() - begin.getTime()); + } + +} + +export function toPixels(value: string): number { + const el = document.createElement('div'); + el.style.position = 'absolute'; + el.style.visibility = 'hidden'; + el.style.width = value; + document.body.appendChild(el); + const pixels = el.offsetWidth; + document.body.removeChild(el); + return pixels; +} + +export class Unique { + + private list: T[] = []; + + get all(): T[] { + return [...this.list]; + } + + constructor(readonly equals: (a: T, b: T) => boolean) { + this.equals = equals; + } + + readonly reset = (list: T[] = []): void => { + this.list.length = 0; + this.add(list); + }; + + readonly add = (item: T | T[] | null | undefined): void => { + if (item === null || item === undefined) { + return; + } + if (item instanceof Array) { + item.forEach(this.add); + return; + } + const index = this.list.findIndex(it => this.equals(it, item)); + if (index < 0) { + this.list.push(item); + } else { + this.list[index] = item; + } + }; + + readonly remove = (item: T | T[] | null | undefined): void => { + if (item === null || item === undefined) { + return; + } + if (item instanceof Array) { + item.forEach(this.add); + return; + } + this.removeIf(it => this.equals(it, item)) + }; + + get length(): number { + return this.list.length; + } + + map(mapper: (item: T) => R): R[] { + return this.list.map(mapper); + } + + * [Symbol.iterator](): Generator { + for (let i = 0; i < this.list.length; i++) { + yield this.list[i]; + } + } + + has(item: T): boolean { + return this.list.some(it => this.equals(it, item)); + } + + removeIf(predicate: Predicate) { + this.list = this.list.filter(not(predicate)); + } + +} + +export type Predicate = (t: T) => boolean; + +export function notNull(value: any): boolean { + return value !== null && value !== undefined; +} + +export function not(predicate: Predicate) { + return (t: T) => !predicate(t); +} diff --git a/src/main/angular/src/app/common/Value.ts b/src/main/angular/src/app/common/Value.ts deleted file mode 100644 index 0a1a5fb..0000000 --- a/src/main/angular/src/app/common/Value.ts +++ /dev/null @@ -1,29 +0,0 @@ -export class Value { - - readonly integer: string; - - readonly fraction: string; - - readonly showDot: boolean; - - constructor( - readonly value: number | null, - decimals: number, - ) { - if (value !== null) { - const parts = (value + '').split('.'); - if (parts.length === 2) { - this.integer = parts[0]; - this.fraction = parts[1].substring(0, decimals).padEnd(decimals, '0'); - } else { - this.integer = parts[0]; - this.fraction = '0'.repeat(decimals); - } - } else { - this.integer = '-'; - this.fraction = ''; - } - this.showDot = value !== null && decimals > 0; - } - -} diff --git a/src/main/angular/src/app/common/checkbox/checkbox.component.html b/src/main/angular/src/app/common/checkbox/checkbox.component.html deleted file mode 100644 index 6dada88..0000000 --- a/src/main/angular/src/app/common/checkbox/checkbox.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/angular/src/app/common/checkbox/checkbox.component.ts b/src/main/angular/src/app/common/checkbox/checkbox.component.ts deleted file mode 100644 index 25aee19..0000000 --- a/src/main/angular/src/app/common/checkbox/checkbox.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; -import {FormsModule} from '@angular/forms'; - -@Component({ - selector: 'app-checkbox', - imports: [ - FormsModule - ], - templateUrl: './checkbox.component.html', - styleUrl: './checkbox.component.less' -}) -export class CheckboxComponent { - - protected _initial: boolean = false; - - protected model: boolean = false; - - @Output() - readonly onChange = new EventEmitter(); - - @Input() - set initial(v: boolean) { - this._initial = v; - this.model = this._initial; - } - - apply() { - if (this.model !== this._initial) { - this.onChange.emit(this.model); - } - } - -} diff --git a/src/main/angular/src/app/common/column/column.component.html b/src/main/angular/src/app/common/column/column.component.html deleted file mode 100644 index eb28ef0..0000000 --- a/src/main/angular/src/app/common/column/column.component.html +++ /dev/null @@ -1,12 +0,0 @@ -
- {{ title === null ? column.title : title }} - @if (sorter.isAscending(column)) { - - } - @if (sorter.isDescending(column)) { - - } - @if (sorter.showCount(column)) { - {{ sorter.indexOf(column) + 1 }} - } -
diff --git a/src/main/angular/src/app/common/column/column.component.less b/src/main/angular/src/app/common/column/column.component.less deleted file mode 100644 index 0171ed4..0000000 --- a/src/main/angular/src/app/common/column/column.component.less +++ /dev/null @@ -1,3 +0,0 @@ -.sub { - font-size: 50%; -} diff --git a/src/main/angular/src/app/common/column/column.component.ts b/src/main/angular/src/app/common/column/column.component.ts deleted file mode 100644 index d92876d..0000000 --- a/src/main/angular/src/app/common/column/column.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; -import {Column} from '../sorter/Column'; -import {Sorter} from '../sorter/Sorter'; - -@Component({ - selector: 'th[app-column]', - imports: [], - templateUrl: './column.component.html', - styleUrl: './column.component.less' -}) -export class ColumnComponent { - - @Input() - column!: Column; - - @Input() - sorter!: Sorter; - - @Input() - title: string | null = null; - - @Output() - readonly onChange: EventEmitter = new EventEmitter(); - - click() { - this.sorter.toggle(this.column); - this.onChange.emit(); - } - -} diff --git a/src/main/angular/src/app/common/number/number.component.html b/src/main/angular/src/app/common/number/number.component.html deleted file mode 100644 index edc699c..0000000 --- a/src/main/angular/src/app/common/number/number.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/angular/src/app/common/number/number.component.less b/src/main/angular/src/app/common/number/number.component.less deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/angular/src/app/common/number/number.component.ts b/src/main/angular/src/app/common/number/number.component.ts deleted file mode 100644 index 29f1b07..0000000 --- a/src/main/angular/src/app/common/number/number.component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; -import {FormsModule} from '@angular/forms'; - -@Component({ - selector: 'app-number', - imports: [ - FormsModule - ], - templateUrl: './number.component.html', - styleUrl: './number.component.less' -}) -export class NumberComponent { - - protected _initial: number | null = null; - - protected edit: boolean = false; - - protected model: string = ""; - - @Output() - readonly onChange = new EventEmitter(); - - @Input() - set initial(v: number | null) { - this._initial = v; - if (!this.edit) { - this.reset(); - } - } - - focus() { - this.edit = true; - } - - apply() { - this.edit = false; - const value = parseFloat(this.model); - const value1 = isNaN(value) ? null : value; - if (value1 !== this._initial) { - this.onChange.emit(value1); - } - } - - cancel() { - this.edit = false; - this.reset(); - } - - private reset() { - this.model = this._initial === null ? "" : this._initial + ""; - } - -} diff --git a/src/main/angular/src/app/common/numberNN/number-n-n.component.html b/src/main/angular/src/app/common/numberNN/number-n-n.component.html deleted file mode 100644 index edc699c..0000000 --- a/src/main/angular/src/app/common/numberNN/number-n-n.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/angular/src/app/common/numberNN/number-n-n.component.less b/src/main/angular/src/app/common/numberNN/number-n-n.component.less deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/angular/src/app/common/numberNN/number-n-n.component.ts b/src/main/angular/src/app/common/numberNN/number-n-n.component.ts deleted file mode 100644 index 6174529..0000000 --- a/src/main/angular/src/app/common/numberNN/number-n-n.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; -import {FormsModule} from '@angular/forms'; - -@Component({ - selector: 'app-number-nn', - imports: [ - FormsModule - ], - templateUrl: './number-n-n.component.html', - styleUrl: './number-n-n.component.less' -}) -export class NumberNNComponent { - - protected _initial!: number; - - protected edit: boolean = false; - - protected model: string = ""; - - @Output() - readonly onChange = new EventEmitter(); - - @Input() - set initial(v: number) { - this._initial = v; - if (!this.edit) { - this.reset(); - } - } - - focus() { - this.edit = true; - } - - apply() { - this.edit = false; - const value = parseFloat(this.model); - if (isNaN(value)) { - this.reset(); - return - } - if (value != this._initial) { - this.onChange.emit(value); - } - } - - cancel() { - this.edit = false; - this.reset(); - } - - private reset() { - this.model = this._initial === null ? "" : this._initial + ""; - } - -} diff --git a/src/main/angular/src/app/common/sorter/Column.ts b/src/main/angular/src/app/common/sorter/Column.ts deleted file mode 100644 index 124d15f..0000000 --- a/src/main/angular/src/app/common/sorter/Column.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Compare} from '../../COMMON'; - -export class Column { - - private constructor( - readonly name: string, - readonly title: string, - readonly compare: Compare, - ) { - // - } - - static create( - name: string, - title: string, - getter: (value: T) => R, - compare: Compare, - ) { - return new Column( - name, - title, - (a, b) => compare(getter(a), getter(b)), - ); - } - -} diff --git a/src/main/angular/src/app/common/sorter/Direction.ts b/src/main/angular/src/app/common/sorter/Direction.ts deleted file mode 100644 index 5a7283d..0000000 --- a/src/main/angular/src/app/common/sorter/Direction.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Direction { - ASC = "ASC", - DESC = "DESC", -} diff --git a/src/main/angular/src/app/common/sorter/Order.ts b/src/main/angular/src/app/common/sorter/Order.ts deleted file mode 100644 index 393adc2..0000000 --- a/src/main/angular/src/app/common/sorter/Order.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {Column} from "./Column"; -import {Direction} from './Direction'; - -export class Order { - - constructor( - public column: Column, - public direction: Direction = Direction.ASC, - ) { - // - } - - toJson(): {} { - return { - property: this.column.name, - direction: this.direction, - }; - } - -} diff --git a/src/main/angular/src/app/common/sorter/Sorter.ts b/src/main/angular/src/app/common/sorter/Sorter.ts deleted file mode 100644 index 984e04d..0000000 --- a/src/main/angular/src/app/common/sorter/Sorter.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {Order} from "./Order"; -import {Column} from "./Column"; -import {Direction} from './Direction'; - -export abstract class Sorter { - - protected constructor( - private _orders: Order[] = [], - ) { - // - } - - get orders(): Order[] { - return this._orders; - } - - readonly compare = (a: T, b: T) => { - for (const order of this._orders) { - const diff = order.column.compare(a, b); - if (diff !== 0) { - return order.direction === Direction.ASC ? diff : -diff; - } - } - return 0; - } - - toggle(column: Column): void { - const index = this.indexOf(column); - if (index < 0) { - this.orders.push(new Order(column)); - } else { - const order = this.orders[index]; - if (order.direction === Direction.ASC) { - order.direction = Direction.DESC; - } else if (order.direction === Direction.DESC) { - this.orders.splice(index, 1); - } - } - } - - get count(): number { - return this._orders.length; - } - - toJson(): {} { - return this.orders.map(o => o.toJson()); - } - - isAscending(column: Column): boolean { - return this.orders.some(o => o.column === column && o.direction === Direction.ASC); - } - - isDescending(column: Column): boolean { - return this.orders.some(o => o.column === column && o.direction === Direction.DESC); - } - - indexOf(column: Column) { - return this.orders.findIndex(o => o.column === column); - } - - showCount(column: Column): boolean { - return this.count > 1 && this.orders.some(o => o.column === column); - } - -} diff --git a/src/main/angular/src/app/common/text/text.component.html b/src/main/angular/src/app/common/text/text.component.html deleted file mode 100644 index 7b8e1d5..0000000 --- a/src/main/angular/src/app/common/text/text.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/angular/src/app/common/text/text.component.less b/src/main/angular/src/app/common/text/text.component.less deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/angular/src/app/common/text/text.component.ts b/src/main/angular/src/app/common/text/text.component.ts deleted file mode 100644 index aa73c8a..0000000 --- a/src/main/angular/src/app/common/text/text.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; -import {FormsModule} from '@angular/forms'; - -@Component({ - selector: 'app-text', - imports: [ - FormsModule - ], - templateUrl: './text.component.html', - styleUrl: './text.component.less' -}) -export class TextComponent { - - protected _initial!: string; - - private edit: boolean = false; - - protected model: string = ""; - - @Output() - readonly onChange = new EventEmitter(); - - @Input() - set initial(v: string) { - this._initial = v; - if (!this.edit) { - this.reset(); - } - } - - focus() { - this.edit = true; - } - - apply() { - this.edit = false; - if (this.model !== this._initial) { - this.onChange.emit(this.model); - } - } - - cancel() { - this.edit = false; - this.reset(); - } - - private reset() { - this.model = this._initial; - } - -} diff --git a/src/main/angular/src/app/config/Config.ts b/src/main/angular/src/app/config/Config.ts deleted file mode 100644 index 74ddf28..0000000 --- a/src/main/angular/src/app/config/Config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {TopicListConfig} from './TopicListConfig'; -import {SeriesListConfig} from './SeriesListConfig'; - -export class Config { - - constructor( - public topicList: TopicListConfig = new TopicListConfig(), - public seriesList: SeriesListConfig = new SeriesListConfig(), - ) { - // - } - - toJson(): {} { - return { - topicList: this.topicList, - seriesList: this.seriesList.toJson(), - }; - } - - static fromJson(json: any): Config { - return new Config( - TopicListConfig.fromJson(json.topicList), - SeriesListConfig.fromJson(json.seriesList), - ); - } - -} diff --git a/src/main/angular/src/app/config/SeriesListConfig.ts b/src/main/angular/src/app/config/SeriesListConfig.ts deleted file mode 100644 index 995bdb5..0000000 --- a/src/main/angular/src/app/config/SeriesListConfig.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {Interval} from '../series/Interval'; -import {SeriesSorter} from '../series/SeriesSorter'; -import {mapNotNull, validateNumber, validateString} from '../COMMON'; - -export class SeriesListConfig { - - constructor( - public interval: Interval | null = null, - public offset: number = 0, - public search: string = "", - public sorter: SeriesSorter = new SeriesSorter(), - ) { - // - } - - toJson(): {} { - return { - interval: this.interval?.name, - offset: this.offset, - search: this.search, - orders: this.sorter.toJson(), - }; - } - - static fromJson(json: any): SeriesListConfig { - return new SeriesListConfig( - mapNotNull(json.interval, Interval.fromJson), - validateNumber(json.offset), - validateString(json.search), - SeriesSorter.fromJson(json.orders), - ); - } - -} diff --git a/src/main/angular/src/app/config/TopicListConfig.ts b/src/main/angular/src/app/config/TopicListConfig.ts deleted file mode 100644 index e5bed42..0000000 --- a/src/main/angular/src/app/config/TopicListConfig.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {validateBoolean} from '../COMMON'; - -export class TopicListConfig { - - constructor( - public hidden: boolean = true, - public used: boolean = true, - public unused: boolean = true, - public ok: boolean = true, - public details: boolean = false, - ) { - // - } - - static fromJson(json: any): TopicListConfig { - return new TopicListConfig( - validateBoolean(json.hidden), - validateBoolean(json.used), - validateBoolean(json.unused), - validateBoolean(json.ok), - validateBoolean(json.details), - ); - } - -} diff --git a/src/main/angular/src/app/config/config.service.ts b/src/main/angular/src/app/config/config.service.ts deleted file mode 100644 index 3ea5ddc..0000000 --- a/src/main/angular/src/app/config/config.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Injectable} from '@angular/core'; -import {Config} from './Config'; -import {ApiService, Next} from '../COMMON'; - -@Injectable({ - providedIn: 'root' -}) -export class ConfigService { - - constructor( - readonly api: ApiService, - ) { - // - } - - get(next: Next) { - this.api.getSingle(["Config", "get"], Config.fromJson, next); - } - - set(config: Config, next: Next) { - this.api.postSingle(["Config", "set"], config.toJson(), Config.fromJson, next); - } - -} diff --git a/src/main/angular/src/app/dashboard/dashboard.component.html b/src/main/angular/src/app/dashboard/dashboard.component.html deleted file mode 100644 index 55a86ca..0000000 --- a/src/main/angular/src/app/dashboard/dashboard.component.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
- -
- @for (plot of plots; track plot.id) { -
- -
- } -
diff --git a/src/main/angular/src/app/dashboard/dashboard.component.less b/src/main/angular/src/app/dashboard/dashboard.component.less deleted file mode 100644 index d4d4632..0000000 --- a/src/main/angular/src/app/dashboard/dashboard.component.less +++ /dev/null @@ -1,12 +0,0 @@ -.plots { - scroll-snap-type: y mandatory; - height: 100vh; - overflow-y: scroll; - scroll-behavior: smooth; - - .plot { - height: 60vw; - scroll-snap-align: start; - max-height: 100vh; - } -} diff --git a/src/main/angular/src/app/dashboard/dashboard.component.ts b/src/main/angular/src/app/dashboard/dashboard.component.ts deleted file mode 100644 index 53b9fee..0000000 --- a/src/main/angular/src/app/dashboard/dashboard.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Component} from '@angular/core'; -import {PlotService} from '../plot/plot.service'; -import {Plot} from '../plot/Plot'; -import {PlotComponent} from '../plot/plot/plot.component'; -import {WeatherComponent} from '../weather/plot/weather-component'; - -@Component({ - selector: 'app-dashboard', - imports: [ - PlotComponent, - WeatherComponent - ], - templateUrl: './dashboard.component.html', - styleUrl: './dashboard.component.less' -}) -export class DashboardComponent { - - constructor( - readonly plotService: PlotService, - ) { - // - } - - protected get plots(): Plot[] { - return this.plotService.list.filter(p => p.dashboard); - } - -} diff --git a/src/main/angular/src/app/location/Location.ts b/src/main/angular/src/app/location/Location.ts new file mode 100644 index 0000000..8df3f22 --- /dev/null +++ b/src/main/angular/src/app/location/Location.ts @@ -0,0 +1,23 @@ +import {validateNumber, validateString} from '../common'; + +export class Location { + + constructor( + readonly id: number, + readonly name: string, + readonly latitude: number, + readonly longitude: number, + ) { + // + } + + static fromJson(json: any): Location { + return new Location( + validateNumber(json.id), + validateString(json.name), + validateNumber(json.latitude), + validateNumber(json.longitude), + ); + } + +} diff --git a/src/main/angular/src/app/location/list/location-list.html b/src/main/angular/src/app/location/list/location-list.html new file mode 100644 index 0000000..d3c30ba --- /dev/null +++ b/src/main/angular/src/app/location/list/location-list.html @@ -0,0 +1,7 @@ +
+ @for (location of list; track location.id) { +
+ {{ location.name }} +
+ } +
diff --git a/src/main/angular/src/app/common/checkbox/checkbox.component.less b/src/main/angular/src/app/location/list/location-list.less similarity index 100% rename from src/main/angular/src/app/common/checkbox/checkbox.component.less rename to src/main/angular/src/app/location/list/location-list.less diff --git a/src/main/angular/src/app/location/list/location-list.ts b/src/main/angular/src/app/location/list/location-list.ts new file mode 100644 index 0000000..784a6a0 --- /dev/null +++ b/src/main/angular/src/app/location/list/location-list.ts @@ -0,0 +1,25 @@ +import {Component, OnInit} from '@angular/core'; +import {LocationService} from '../location-service'; +import {Location} from '../Location'; + +@Component({ + selector: 'app-location-list', + imports: [], + templateUrl: './location-list.html', + styleUrl: './location-list.less', +}) +export class LocationList implements OnInit { + + protected list: Location[] = []; + + constructor( + readonly locationService: LocationService, + ) { + // + } + + ngOnInit(): void { + this.locationService.findAll(list => this.list = list); + } + +} diff --git a/src/main/angular/src/app/location/location-service.ts b/src/main/angular/src/app/location/location-service.ts new file mode 100644 index 0000000..95671d9 --- /dev/null +++ b/src/main/angular/src/app/location/location-service.ts @@ -0,0 +1,18 @@ +import {Injectable} from '@angular/core'; +import {ApiService, CrudService, Next, WebsocketService} from '../common'; +import {Location} from './Location' + +@Injectable({ + providedIn: 'root' +}) +export class LocationService extends CrudService { + + constructor(api: ApiService, ws: WebsocketService) { + super(api, ws, ['Location'], Location.fromJson); + } + + findAll(next: Next) { + this.getList(['findAll'], next); + } + +} diff --git a/src/main/angular/src/app/plot/Group.ts b/src/main/angular/src/app/plot/Group.ts deleted file mode 100644 index 65093ab..0000000 --- a/src/main/angular/src/app/plot/Group.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum Group { - NONE = "NONE", - FIVE_OF_DAY = "FIVE_OF_DAY", - HOUR_OF_DAY = "HOUR_OF_DAY", - HOUR_OF_WEEK = "HOUR_OF_WEEK", - HOUR_OF_MONTH = "HOUR_OF_MONTH", - DAY_OF_WEEK = "DAY_OF_WEEK", - DAY_OF_MONTH = "DAY_OF_MONTH", - DAY_OF_YEAR = "DAY_OF_YEAR", - WEEK_OF_YEAR = "WEEK_OF_YEAR", -} diff --git a/src/main/angular/src/app/plot/Plot.ts b/src/main/angular/src/app/plot/Plot.ts deleted file mode 100644 index bf22884..0000000 --- a/src/main/angular/src/app/plot/Plot.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {ID, validateBoolean, validateListIndexed, validateNumber, validateString} from '../COMMON'; -import {Interval} from '../series/Interval'; -import {Axis} from './axis/Axis'; - -export class Plot extends ID { - - readonly axes: Axis[]; - - constructor( - readonly id: number, - readonly version: number, - readonly deleted: boolean, - readonly name: string, - readonly interval: Interval, - readonly offset: number, - readonly duration: number, - readonly dashboard: boolean, - readonly position: number, - axes: any[], - ) { - super(); - this.axes = validateListIndexed(axes, ((json, index) => Axis.fromJson(this, index, json))); - } - - static fromJson(json: any): Plot { - return new Plot( - validateNumber(json.id), - validateNumber(json.version), - validateBoolean(json.deleted), - validateString(json.name), - Interval.fromJson(json.interval), - validateNumber(json.offset), - validateNumber(json.duration), - validateBoolean(json.dashboard), - validateNumber(json.position), - json.axes, - ); - } - - static comparePosition(a: Plot, b: Plot) { - return a.position - b.position; - } - -} diff --git a/src/main/angular/src/app/plot/axis/Axis.ts b/src/main/angular/src/app/plot/axis/Axis.ts deleted file mode 100644 index cffd48f..0000000 --- a/src/main/angular/src/app/plot/axis/Axis.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {mapNotNull, validateBoolean, validateList, validateNumber, validateString} from "../../COMMON"; -import {Graph} from './graph/Graph'; -import {Plot} from '../Plot'; - -export class Axis { - - readonly graphs: Graph[]; - - constructor( - readonly plot: Plot, - readonly id: number, - readonly version: number, - readonly index: number, - readonly name: string, - readonly unit: string, - readonly visible: boolean, - readonly right: boolean, - readonly min: number | null, - readonly minHard: boolean, - readonly max: number | null, - readonly maxHard: boolean, - graphs: any[], - ) { - this.graphs = validateList(graphs, json => Graph.fromJson(this, json)) - } - - static fromJson(plot: Plot, index: number, json: any): Axis { - return new Axis( - plot, - validateNumber(json.id), - validateNumber(json.version), - index, - validateString(json.name), - validateString(json.unit), - validateBoolean(json.visible), - validateBoolean(json.right), - mapNotNull(json.min, validateNumber), - validateBoolean(json.minHard), - mapNotNull(json.max, validateNumber), - validateBoolean(json.maxHard), - json.graphs, - ); - } - -} diff --git a/src/main/angular/src/app/plot/axis/graph/Graph.ts b/src/main/angular/src/app/plot/axis/graph/Graph.ts deleted file mode 100644 index 386eb95..0000000 --- a/src/main/angular/src/app/plot/axis/graph/Graph.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {Series} from "../../../series/Series"; -import {Group} from "../../Group"; -import {mapNotNull, validateBoolean, validateEnum, validateNumber, validateString} from "../../../COMMON"; -import {Axis} from '../Axis'; -import {SeriesType} from '../../../series/SeriesType'; -import {GraphType} from './GraphType'; - -export enum GraphOperation { - MINUS = 'MINUS', - PLUS = 'PLUS', - DIVIDE = 'DIVIDE', -} - -export function listGraphOperation() { - return Object.keys(GraphOperation); -} - -export class Graph { - - readonly showMin: boolean; - - readonly showMid: boolean; - - readonly showMax: boolean; - - constructor( - readonly axis: Axis, - readonly id: number, - readonly version: number, - readonly series: Series, - readonly factor: number, - readonly operation: GraphOperation, - readonly series2: Series | null, - readonly factor2: number, - readonly name: string, - readonly visible: boolean, - readonly type: GraphType, - readonly fill: string, - readonly color: string, - readonly group: Group, - readonly stack: string, - readonly min: boolean, - readonly max: boolean, - readonly avg: boolean, - ) { - this.showMin = this.series.type === SeriesType.VARYING && this.min; - this.showMid = this.series.type === SeriesType.BOOL || this.series.type === SeriesType.DELTA || this.avg; - this.showMax = this.series.type === SeriesType.VARYING && this.max; - } - - static fromJson(axis: Axis, json: any): Graph { - return new Graph( - axis, - validateNumber(json.id), - validateNumber(json.version), - Series.fromJson(json.series), - validateNumber(json.factor), - validateEnum(json.operation, GraphOperation), - mapNotNull(json.series2, Series.fromJson), - validateNumber(json.factor2), - validateString(json.name), - validateBoolean(json.visible), - GraphType.fromJson(json.type), - validateString(json.fill), - validateString(json.color), - validateString(json.group) as Group, - validateString(json.stack), - validateBoolean(json.min), - validateBoolean(json.max), - validateBoolean(json.avg), - ); - } - -} diff --git a/src/main/angular/src/app/plot/axis/graph/GraphType.ts b/src/main/angular/src/app/plot/axis/graph/GraphType.ts deleted file mode 100644 index 9805b50..0000000 --- a/src/main/angular/src/app/plot/axis/graph/GraphType.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {validateString} from "../../../COMMON"; - -export class GraphType { - - protected static _values: GraphType[] = []; - - static readonly LINE = new GraphType("LINE", "line", "Linie"); - - static readonly BAR = new GraphType("BAR", "bar", "Balken"); - - private constructor( - readonly jsonName: string, - readonly chartJsName: "line" | "bar", - readonly display: string, - ) { - GraphType._values.push(this); - } - - static fromJson(json: any): GraphType { - const name = validateString(json) - const graphType = GraphType._values.filter(i => i.jsonName === name)[0]; - if (!graphType) { - throw new Error(`Not an GraphType: ${JSON.stringify(json)}`); - } - return graphType; - } - - static get values(): GraphType[] { - return GraphType._values; - } - -} diff --git a/src/main/angular/src/app/plot/editor/plot-editor.component.html b/src/main/angular/src/app/plot/editor/plot-editor.component.html deleted file mode 100644 index b52d122..0000000 --- a/src/main/angular/src/app/plot/editor/plot-editor.component.html +++ /dev/null @@ -1,244 +0,0 @@ -
-
- - - - -
- -
- -
- - @if (plot) { - -
- - - - - - - - - - - - - - - - - -
NameIntervallVerschiebungDauerDashPosition
- - - - - - - - - - - -
-
- - -
- - - - - - - - - - - - - - @for (axis of plot.axes; track axis.id) { - - - - - - - - - - - - - } -
- - - - NameEinheitrechtsminfestmaxfest 
- - - Y{{ axis.index + 1 }} - - - - - - - - - - - - - - - - -   - -
- - - -
- - -
- - - - - - - - - - - - - - - - - @for (axis of plot.axes; track axis.id) { - @for (graph of axis.graphs; track graph.id) { - - - - - - - - - - - - - @if (graph.series.type === SeriesType.VARYING) { - - - - } @else { - @if (graph.series.type === SeriesType.BOOL) { - - } @else { - - } - } - - - - } - } -
- - NameSerieFaktorTypFarbeAggregatStackminmaxAchse 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - BooleanDelta - - - -
-
- - } - -
diff --git a/src/main/angular/src/app/plot/editor/plot-editor.component.less b/src/main/angular/src/app/plot/editor/plot-editor.component.less deleted file mode 100644 index b518dc4..0000000 --- a/src/main/angular/src/app/plot/editor/plot-editor.component.less +++ /dev/null @@ -1,43 +0,0 @@ -.header { - display: flex; -} - -.plot { - height: 60vw; - max-height: 50vh; -} - -.subSeries { - text-align: center; -} - -.vertical { - writing-mode: vertical-rl; - rotate: 180deg; - text-align: left; -} - -.Section { - border-top: 1px solid gray; - padding: 1em 0.5em; - - table { - margin-bottom: 0.5em; - } -} - -.PlotDetails { - -} - -.Axes { - background-color: lightgray; -} - -.Graphs { - -} - -button { - padding: 0.5em; -} diff --git a/src/main/angular/src/app/plot/editor/plot-editor.component.ts b/src/main/angular/src/app/plot/editor/plot-editor.component.ts deleted file mode 100644 index 27b78ca..0000000 --- a/src/main/angular/src/app/plot/editor/plot-editor.component.ts +++ /dev/null @@ -1,145 +0,0 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {BarController, BarElement, CategoryScale, Chart, Filler, Legend, LinearScale, LineController, LineElement, PointElement, TimeScale, Title, Tooltip} from 'chart.js'; -import {SeriesService} from "../../series/series.service"; -import {SeriesType} from '../../series/SeriesType'; -import {PlotService} from '../plot.service'; -import {Plot} from '../Plot'; -import {FormsModule} from '@angular/forms'; -import {FaIconComponent} from '@fortawesome/angular-fontawesome'; -import {faCopy, faEye, faListAlt} from '@fortawesome/free-regular-svg-icons'; -import {TextComponent} from '../../common/text/text.component'; -import {NumberComponent} from '../../common/number/number.component'; -import {CheckboxComponent} from '../../common/checkbox/checkbox.component'; -import {Group} from '../Group'; -import {NumberNNComponent} from '../../common/numberNN/number-n-n.component'; -import {Subscription} from 'rxjs'; -import {Interval} from '../../series/Interval'; -import {faChartLine, faPlus, faTrash} from '@fortawesome/free-solid-svg-icons'; -import {Location} from '@angular/common'; -import {PlotComponent} from '../plot/plot.component'; -import {ActivatedRoute} from '@angular/router'; -import {GraphType} from '../axis/graph/GraphType'; -import {GraphOperation, listGraphOperation} from '../axis/graph/Graph'; - -Chart.register( - CategoryScale, - LinearScale, - BarController, - BarElement, - LineController, - LineElement, - PointElement, - Title, - Tooltip, - Legend, - TimeScale, - Filler, -); - -@Component({ - selector: 'app-plot-editor', - imports: [ - FormsModule, - FaIconComponent, - TextComponent, - NumberComponent, - CheckboxComponent, - NumberNNComponent, - PlotComponent - ], - templateUrl: './plot-editor.component.html', - styleUrl: './plot-editor.component.less' -}) -export class PlotEditor implements OnInit, OnDestroy { - - protected readonly GraphType = GraphType; - - protected readonly GraphOperation = GraphOperation; - - protected readonly listGraphOperation = listGraphOperation; - - protected readonly SeriesType = SeriesType; - - protected readonly Interval = Interval; - - protected readonly faEye = faEye; - - protected readonly faListAlt = faListAlt; - - protected readonly faCopy = faCopy; - - protected readonly faPlus = faPlus; - - protected readonly faTrash = faTrash; - - protected readonly faChartLine = faChartLine; - - private id: number = NaN; - - private _plot: Plot | null = null; - - private readonly subs: Subscription[] = []; - - constructor( - readonly activatedRoute: ActivatedRoute, - readonly seriesService: SeriesService, - readonly plotService: PlotService, - readonly location: Location - ) { - // - } - - get plot(): Plot | null { - return this._plot; - } - - set plot(value: Plot | null) { - this._plot = value; - if (this._plot) { - this.id = this._plot.id; - } - if (isNaN(this.id)) { - this.location.go("/PlotEditor"); - } else { - this.location.go("/PlotEditor/" + this.id); - } - } - - ngOnInit(): void { - this.subs.push(this.activatedRoute.params.subscribe(params => { - this.id = parseInt(params['id']); - this.loadPlot(); - this.subs.push(this.plotService.subscribeListItems(fresh => { - if (fresh.deleted) { - if (fresh.hasId(this.id)) { - this.id = NaN; - } - } else if (isNaN(this.id)) { - } - this.loadPlot(); - })); - })); - } - - private loadPlot() { - if (isNaN(this.id)) { - this.plot = this.plotService.list[0] || null; - this.id = this.plot?.id || NaN; - } else { - this.plot = this.plotService.byId(this.id) || null; - } - } - - ngOnDestroy(): void { - this.subs.forEach(subscription => subscription.unsubscribe()); - } - - protected readonly setPlot = (plot: Plot): void => { - this.plot = plot; - }; - - protected groups(): string[] { - return Object.keys(Group); - } - -} diff --git a/src/main/angular/src/app/plot/plot.service.ts b/src/main/angular/src/app/plot/plot.service.ts deleted file mode 100644 index e6410b0..0000000 --- a/src/main/angular/src/app/plot/plot.service.ts +++ /dev/null @@ -1,165 +0,0 @@ -import {Injectable} from '@angular/core'; -import {ApiService, EntityListService, Next} from '../COMMON'; -import {Plot} from './Plot'; -import {Axis} from './axis/Axis'; -import {Graph, GraphOperation} from './axis/graph/Graph'; -import {Group} from './Group'; -import {Interval} from '../series/Interval'; -import {GraphType} from './axis/graph/GraphType'; - -@Injectable({ - providedIn: 'root' -}) -export class PlotService extends EntityListService { - - constructor( - api: ApiService, - ) { - super(api, ["Plot"], Plot.fromJson, Plot.equals, Plot.comparePosition); - } - - plotCreate(next?: Next): void { - this.getSingle(["create"], next); - } - - plotDuplicate(plot: Plot, next?: Next): void { - this.getSingle([plot.id, "duplicate"], next); - } - - plotDelete(plot: Plot, next?: Next): void { - this.getSingle([plot.id, "delete"], next); - } - - plotAddAxis(plot: Plot, next?: Next): void { - this.getSingle([plot.id, 'addAxis'], next); - } - - plotName(plot: Plot, value: string, next?: Next): void { - this.postSingle([plot.id, 'name'], value, next); - } - - plotInterval(plot: Plot, value: Interval, next?: Next): void { - this.postSingle([plot.id, 'interval'], value.name, next); - } - - plotOffset(plot: Plot, value: number, next?: Next): void { - this.postSingle([plot.id, 'offset'], value, next); - } - - plotDuration(plot: Plot, value: number, next?: Next): void { - this.postSingle([plot.id, 'duration'], value, next); - } - - plotDashboard(plot: Plot, value: boolean, next?: Next): void { - this.postSingle([plot.id, 'dashboard'], value, next); - } - - plotPosition(plot: Plot, value: number, next?: Next): void { - this.postSingle([plot.id, 'position'], value, next); - } - - axisAddGraph(axis: Axis, next?: Next): void { - this.getSingle(['Axis', axis.id, 'addGraph'], next); - } - - axisDelete(axis: Axis, next?: Next): void { - this.getSingle(['Axis', axis.id, 'delete'], next); - } - - axisVisible(axis: Axis, value: boolean, next?: Next): void { - this.postSingle(['Axis', axis.id, 'visible'], value, next); - } - - axisName(axis: Axis, value: string, next?: Next): void { - this.postSingle(['Axis', axis.id, 'name'], value, next); - } - - axisUnit(axis: Axis, value: string, next?: Next): void { - this.postSingle(['Axis', axis.id, 'unit'], value, next); - } - - axisRight(axis: Axis, value: boolean, next?: Next): void { - this.postSingle(['Axis', axis.id, 'right'], value, next); - } - - axisMin(axis: Axis, value: number | null, next?: Next): void { - this.postSingle(['Axis', axis.id, 'min'], value, next); - } - - axisMinHard(axis: Axis, value: boolean, next?: Next): void { - this.postSingle(['Axis', axis.id, 'minHard'], value, next); - } - - axisMax(axis: Axis, value: number | null, next?: Next): void { - this.postSingle(['Axis', axis.id, 'max'], value, next); - } - - axisMaxHard(axis: Axis, value: boolean, next?: Next): void { - this.postSingle(['Axis', axis.id, 'maxHard'], value, next); - } - - graphDelete(graph: Graph, next?: Next): void { - this.getSingle(['Graph', graph.id, 'delete'], next); - } - - graphVisible(graph: Graph, value: boolean, next?: Next): void { - this.postSingle(['Graph', graph.id, 'visible'], value, next); - } - - graphType(graph: Graph, value: GraphType, next?: Next): void { - this.postSingle(['Graph', graph.id, 'type'], value.jsonName, next); - } - - graphName(graph: Graph, value: string, next?: Next): void { - this.postSingle(['Graph', graph.id, 'name'], value, next); - } - - graphSeries(graph: Graph, value: number, next?: Next): void { - this.postSingle(['Graph', graph.id, 'series'], value, next); - } - - graphFactor(graph: Graph, value: number, next?: Next): void { - this.postSingle(['Graph', graph.id, 'factor'], value, next); - } - - graphOperation(graph: Graph, value: GraphOperation, next?: Next): void { - this.postSingle(['Graph', graph.id, 'operation'], value, next); - } - - graphSeries2(graph: Graph, value: number, next?: Next): void { - this.postSingle(['Graph', graph.id, 'series2'], value, next); - } - - graphFactor2(graph: Graph, value: number, next?: Next): void { - this.postSingle(['Graph', graph.id, 'factor2'], value, next); - } - - graphColor(graph: Graph, value: string, next?: Next): void { - this.postSingle(['Graph', graph.id, 'color'], value, next); - } - - graphGroup(graph: Graph, value: Group, next?: Next): void { - this.postSingle(['Graph', graph.id, 'group'], value, next); - } - - graphStack(graph: Graph, value: string, next?: Next): void { - this.postSingle(['Graph', graph.id, 'stack'], value, next); - } - - graphMin(graph: Graph, value: boolean, next?: Next): void { - this.postSingle(['Graph', graph.id, 'min'], value, next); - } - - graphMax(graph: Graph, value: boolean, next?: Next): void { - this.postSingle(['Graph', graph.id, 'max'], value, next); - } - - graphAvg(graph: Graph, value: boolean, next?: Next): void { - this.postSingle(['Graph', graph.id, 'avg'], value, next); - } - - graphAxis(graph: Graph, value: number, next?: Next): void { - this.postSingle(['Graph', graph.id, 'axis'], value, next); - } - -} diff --git a/src/main/angular/src/app/plot/plot/plot.component.html b/src/main/angular/src/app/plot/plot/plot.component.html deleted file mode 100644 index 4585a0f..0000000 --- a/src/main/angular/src/app/plot/plot/plot.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/src/main/angular/src/app/plot/plot/plot.component.less b/src/main/angular/src/app/plot/plot/plot.component.less deleted file mode 100644 index cbe226c..0000000 --- a/src/main/angular/src/app/plot/plot/plot.component.less +++ /dev/null @@ -1,4 +0,0 @@ -.container { - width: 100%; - height: 100%; -} diff --git a/src/main/angular/src/app/plot/plot/plot.component.ts b/src/main/angular/src/app/plot/plot/plot.component.ts deleted file mode 100644 index fb00123..0000000 --- a/src/main/angular/src/app/plot/plot/plot.component.ts +++ /dev/null @@ -1,275 +0,0 @@ -import {AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild} from '@angular/core'; -import {BarController, BarElement, CategoryScale, Chart, ChartDataset, Filler, Legend, LinearScale, LineController, LineElement, PointElement, TimeScale, Title, Tooltip} from 'chart.js'; -import {toAvg, toBool, toDelta, toMax, toMin} from '../../series/MinMaxAvg'; -import {SeriesType} from '../../series/SeriesType'; -import {Plot} from '../Plot'; -import {FormsModule} from '@angular/forms'; -import {NTU, UTN} from '../../COMMON'; -import {Graph} from '../axis/graph/Graph'; -import {Axis} from '../axis/Axis'; -import {Subscription} from 'rxjs'; -import {DeltaService} from '../../series/delta/delta-service'; -import {BoolService} from '../../series/bool/bool-service'; -import {VaryingService} from '../../series/varying/varying-service'; -import {Interval} from '../../series/Interval'; -import {SeriesService} from '../../series/series.service'; -import {GraphType} from '../axis/graph/GraphType'; -import {Varying} from '../../series/varying/Varying'; -import {Delta} from '../../series/delta/Delta'; -import {Bool} from '../../series/bool/Bool'; - -type Dataset = ChartDataset[][number]; - -Chart.register( - CategoryScale, - LinearScale, - BarController, - BarElement, - LineController, - LineElement, - PointElement, - Title, - Tooltip, - Legend, - TimeScale, - Filler, -); - -export function unitInBrackets(unit: string): string { - if (!unit) { - return ''; - } - return ` [${unit}]`; -} - -@Component({ - selector: 'app-plot', - imports: [ - FormsModule - ], - templateUrl: './plot.component.html', - styleUrl: './plot.component.less' -}) -export class PlotComponent implements AfterViewInit, OnDestroy { - - protected readonly SeriesType = SeriesType; - - protected readonly Interval = Interval; - - @ViewChild('chartCanvas') - protected canvasRef!: ElementRef; - - @ViewChild('container') - protected chartContainer!: ElementRef; - - private readonly subs: Subscription[] = []; - - private chart!: Chart; - - protected _plot: Plot | null = null; - - get plot(): Plot | null { - return this._plot; - } - - constructor( - readonly seriesService: SeriesService, - readonly boolService: BoolService, - readonly deltaService: DeltaService, - readonly varyingService: VaryingService, - ) { - } - - ngOnDestroy(): void { - this.subs.forEach(subscription => subscription.unsubscribe()); - this.chart.destroy(); - } - - ngAfterViewInit(): void { - this.chart = new Chart(this.canvasRef.nativeElement, { - type: 'line', - data: { - datasets: [], - }, - options: { - responsive: true, - maintainAspectRatio: false, - animation: false, - plugins: { - legend: { - display: true, - labels: { - usePointStyle: true, - }, - }, - tooltip: { - position: 'nearest', - mode: 'x', - usePointStyle: true, - }, - }, - scales: { - x: { - type: 'time', - }, - } - } - }); - this.plot = this.plot; - } - - @Input() - set plot(plot: Plot | null) { - this._plot = UTN(plot); - - this.subs.forEach(s => s.unsubscribe()); - this.subs.length = 0; - if (!this.chart?.options?.plugins) { - return; - } - - for (const key in this.chart.options.scales) { - if (key.startsWith("y")) { - delete this.chart.options.scales[key]; - } - } - this.chart.data.labels = []; - this.chart.data.datasets = []; - - this.chart.update(); - if (!this.plot) { - return; - - } - this.chart.options.plugins.title = { - display: !!this.plot.name, - text: this.plot.name, - }; - for (const axis of this.plot.axes) { - this.buildAxis(axis); - } - }; - - private buildAxis(axis: Axis): void { - if (!this.chart.options.scales) { - return; - } - const name = axis.name + unitInBrackets(axis.unit); - this.chart.options.scales["y" + axis.id] = { - display: axis.visible, - title: { - display: !!name, - text: name, - }, - suggestedMin: NTU(axis.min), - suggestedMax: NTU(axis.max), - min: axis.minHard ? NTU(axis.min) : undefined, - max: axis.maxHard ? NTU(axis.max) : undefined, - position: axis.right ? 'right' : 'left', - }; - for (const graph of axis.graphs) { - this.buildGraph(graph); - } - } - - private buildGraph(graph: Graph): void { - if (!graph.visible) { - return; - } - const midSuffix = graph.series.type === SeriesType.DELTA || graph.series.type === SeriesType.BOOL ? "" : " Ø" - const min: Dataset = graph.showMin ? this.newDataset(graph, " min", false) : null; - const mid: Dataset = graph.showMid ? this.newDataset(graph, midSuffix, min ? '-1' : graph.series.type === SeriesType.BOOL) : null; - const max: Dataset = graph.showMax ? this.newDataset(graph, " max", min || mid ? '-1' : false) : null; - switch (graph.series.type) { - case SeriesType.BOOL: - this.subs.push(this.boolService.subscribe(bool => this.updateBool(graph, bool, mid), [graph.series.id])); - break; - case SeriesType.DELTA: - this.subs.push(this.deltaService.subscribe(delta => this.updateDelta(graph, delta, mid), [graph.series.id, graph.axis.plot.interval.name])); - break; - case SeriesType.VARYING: - this.subs.push(this.varyingService.subscribe(varying => this.updateVarying(graph, varying, min, mid, max), [graph.series.id, graph.axis.plot.interval.name])); - break; - } - this.points(graph, min, mid, max); - } - - private newDataset(graph: Graph, suffix: string, fill: string | number | boolean): Dataset { - this.chart.data.datasets.push({ - data: [], - label: `${graph.name || graph.series.name}${suffix}${unitInBrackets(graph.series.unit)}`, - type: graph.type.chartJsName, - fill: fill || (graph.stack ? 'stack' : false), - stack: graph.stack || undefined, - borderWidth: 1, - barThickness: 'flex', - borderColor: graph.color, - pointRadius: 0, // graph.series.type === SeriesType.BOOL ? 0 : 4, - pointBackgroundColor: graph.color, - backgroundColor: graph.color + "33", - yAxisID: "y" + graph.axis.id, - pointStyle: graph.type === GraphType.BAR || graph.series.type === SeriesType.BOOL ? 'rect' : 'crossRot', - spanGaps: graph.series.type === SeriesType.BOOL ? Infinity : graph.axis.plot.interval.spanGaps, - }); - return this.chart.data.datasets[this.chart.data.datasets.length - 1]; - } - - private points(graph: Graph, min: Dataset, mid: Dataset, max: Dataset): void { - this.seriesService.oneSeriesPoints( - graph, - points => { - if (graph.series.type === SeriesType.BOOL) { - mid.data = toBool(points); - } else if (graph.series.type === SeriesType.DELTA) { - mid.data = toDelta(points); - } else if (graph.series.type === SeriesType.VARYING) { - if (min) { - min.data = toMin(points); - } - if (max) { - max.data = toMax(points); - } - if (mid) { - mid.data = toAvg(points); - } - } - this.chart.update(); - }, - ); - } - - private updateBool(graph: Graph, bool: Bool, mid: Dataset): void { - this.updatePoint(graph, mid, bool.date, bool.state ? 1 : 0); - } - - private updateDelta(graph: Graph, delta: Delta, mid: Dataset): void { - this.updatePoint(graph, mid, delta.date, delta.last - delta.first); - } - - private updateVarying(graph: Graph, varying: Varying, min: Dataset | null, avg: Dataset | null, max: Dataset | null): void { - if (min) { - this.updatePoint(graph, min, varying.date, varying.min); - } - if (avg) { - this.updatePoint(graph, avg, varying.date, varying.avg); - } - if (max) { - this.updatePoint(graph, max, varying.date, varying.max); - } - } - - private updatePoint(graph: Graph, dataset: Dataset, date: Date, y: number): void { - const x = date.getTime(); - const point = dataset.data.filter((p: PointElement) => p.x === x)[0]; - if (point) { - if (point.y !== y) { - point.y = y * graph.factor; // TODO this is wrong. we need to take GraphOperation into account - this.chart.update(); - } - } else { - dataset.data.push({x: x, y: y}); // TODO check if this is a LIVE/SCROLLING plot (right end of plot is 'now') - this.chart.update(); - } - } - -} diff --git a/src/main/angular/src/app/series/AllSeriesPointRequest.ts b/src/main/angular/src/app/series/AllSeriesPointRequest.ts deleted file mode 100644 index 083512b..0000000 --- a/src/main/angular/src/app/series/AllSeriesPointRequest.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {Interval} from "./Interval"; -import {validateNumber} from '../COMMON'; - -export class AllSeriesPointRequest { - - constructor( - readonly interval: Interval, - readonly offset: number, - ) { - // - } - - toJson(): {} { - return { - interval: this.interval.name, - offset: this.offset, - }; - } - - static fromJson(json: any): AllSeriesPointRequest { - return new AllSeriesPointRequest( - Interval.fromJson(json.interval), - validateNumber(json.offset), - ); - } - -} diff --git a/src/main/angular/src/app/series/AllSeriesPointResponse.ts b/src/main/angular/src/app/series/AllSeriesPointResponse.ts deleted file mode 100644 index 0add006..0000000 --- a/src/main/angular/src/app/series/AllSeriesPointResponse.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {mapNotNull, validateBoolean, validateDate, validateList, validateNumber} from "../COMMON"; -import {AllSeriesPointRequest} from './AllSeriesPointRequest'; -import {Series} from './Series'; -import {Value} from '../common/Value'; - -export class AllSeriesPointResponseEntry { - - readonly min: Value; - - readonly avg: Value; - - readonly max: Value; - - readonly first: Value; - - readonly last: Value; - - constructor( - readonly series: Series, - min: number | null, - avg: number | null, - max: number | null, - first: number | null, - last: number | null, - readonly state: boolean | null, - readonly terminated: boolean | null, - ) { - this.min = new Value(min, series.decimals); - this.max = new Value(max, series.decimals); - this.first = new Value(max, series.decimals); - this.last = new Value(max, series.decimals); - if (avg !== null) { - this.avg = new Value(avg, series.decimals); - } else if (first !== null && last !== null) { - this.avg = new Value(last - first, series.decimals); - } else { - this.avg = new Value(null, series.decimals); - } - } - - static fromJson(json: any): AllSeriesPointResponseEntry { - return new AllSeriesPointResponseEntry( - Series.fromJson(json.series), - mapNotNull(json.point?.min, validateNumber), - mapNotNull(json.point?.avg, validateNumber), - mapNotNull(json.point?.max, validateNumber), - mapNotNull(json.point?.first, validateNumber), - mapNotNull(json.point?.last, validateNumber), - mapNotNull(json.state, validateBoolean), - mapNotNull(json.terminated, validateBoolean), - ); - } - -} - -export class AllSeriesPointResponse { - - constructor( - readonly request: AllSeriesPointRequest, - readonly date: Date, - readonly seriesPoints: AllSeriesPointResponseEntry[], - ) { - // - } - - static fromJson(json: any): AllSeriesPointResponse { - return new AllSeriesPointResponse( - AllSeriesPointRequest.fromJson(json.request), - validateDate(json.request?.first), - validateList(json.seriesPoints, AllSeriesPointResponseEntry.fromJson), - ); - } - -} diff --git a/src/main/angular/src/app/series/Interval.ts b/src/main/angular/src/app/series/Interval.ts deleted file mode 100644 index 383a915..0000000 --- a/src/main/angular/src/app/series/Interval.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {validateString} from "../COMMON"; - -export class Interval { - - protected static _values: Interval[] = []; - - static readonly FIVE = new Interval("FIVE", "5 Minuten", 5 * 60 * 1000); - - static readonly HOUR = new Interval("HOUR", "Stunden", 60 * 60 * 1000); - - static readonly DAY = new Interval("DAY", "Tage", 24 * 60 * 60 * 1000); - - static readonly WEEK = new Interval("WEEK", "Wochen", 7 * 24 * 60 * 60 * 1000); - - static readonly MONTH = new Interval("MONTH", "Monate", 31 * 24 * 60 * 60 * 1000); - - static readonly YEAR = new Interval("YEAR", "Jahre", 366 * 24 * 60 * 60 * 1000); - - private constructor( - readonly name: string, - readonly display: string, - readonly spanGaps: number, - ) { - Interval._values.push(this); - } - - static fromJson(json: any): Interval { - const name = validateString(json) - const interval = Interval._values.filter(i => i.name === name)[0]; - if (!interval) { - throw new Error(`Not an Interval: ${JSON.stringify(json)}`); - } - return interval; - } - - static get values(): Interval[] { - return Interval._values; - } - -} - diff --git a/src/main/angular/src/app/series/MinMaxAvg.ts b/src/main/angular/src/app/series/MinMaxAvg.ts deleted file mode 100644 index 3bed60c..0000000 --- a/src/main/angular/src/app/series/MinMaxAvg.ts +++ /dev/null @@ -1,92 +0,0 @@ -export class Point { - - constructor( - public x: number, - public y: number, - ) { - // - } - -} - -export function toBool(points: number[][]): Point[] { - const result = []; - let postPone: Point | null = null; - for (const p of points) { - const state = p[2] > 0; - const begin = { - x: p[0] * 1000, - y: state ? 1 : 0, - }; - const end = { - x: p[1] * 1000, - y: state ? 1 : 0, - }; - if (postPone) { - postPone.x = begin.x; - result.push(postPone); - postPone = null; - } - result.push(begin); - if (begin.x !== end.x) { - result.push(end); - } else { - postPone = end; - } - } - if (postPone) { - // postPone.x = Date.now(); - result.push(postPone); - } - return result; -} - -export function toDelta(points: number[][]): Point[] { - const result = []; - for (const p of points) { - result.push({ - x: p[0] * 1000, - y: (p[1]), - }); - } - return result; -} - -export function toMin(points: number[][]): Point[] { - const result = []; - for (const p of points) { - result.push({ - x: p[0] * 1000, - y: p[1], - }); - } - return result; -} - -export function toMax(points: number[][]): Point[] { - const result = []; - for (const p of points) { - result.push({ - x: p[0] * 1000, - y: p[2], - }); - } - return result; -} - -export function toAvg(points: number[][]): Point[] { - const result = []; - for (const p of points) { - result.push({ - x: p[0] * 1000, - y: p[3], - }); - } - return result; -} - -export enum MinMaxAvg { - MIN = "MIN", - MAX = "MAX", - AVG = "AVG", -} diff --git a/src/main/angular/src/app/series/Series.ts b/src/main/angular/src/app/series/Series.ts deleted file mode 100644 index 1558d3b..0000000 --- a/src/main/angular/src/app/series/Series.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ID, mapNotNull, validateBoolean, validateDate, validateNumber, validateString} from "../COMMON"; -import {SeriesType} from './SeriesType'; -import {formatNumber} from '@angular/common'; -import {Value} from '../common/Value'; - -export class Series extends ID { - - readonly value: Value; - - constructor( - readonly id: number, - readonly version: number, - readonly deleted: boolean, - readonly name: string, - readonly unit: string, - readonly type: SeriesType, - readonly decimals: number, - readonly expectedEverySeconds: number, - readonly date: Date | null, - value: number | null, - ) { - super(); - this.value = new Value(value, this.decimals); - } - - static fromJson(json: any): Series { - return new Series( - validateNumber(json.id), - validateNumber(json.version), - validateBoolean(json.deleted), - validateString(json.name), - validateString(json.unit), - validateString(json.type) as SeriesType, - validateNumber(json.decimals), - validateNumber(json.expectedEverySeconds), - mapNotNull(json.last, validateDate), - mapNotNull(json.value, validateNumber), - ); - } - - get digitString(): string { - return `0.${this.decimals}-${this.decimals}`; - } - - get valueString(): string { - const result = (this.value.value === null ? "-" : this.type === SeriesType.BOOL ? this.value.value > 0 ? "EIN" : "AUS" : formatNumber(this.value.value, "de-DE", this.digitString)) + ""; - if (this.unit) { - return result + " " + this.unit; - } - return result; - } - - static compareName(a: Series, b: Series) { - return a.name.localeCompare(b.name); - } - - isOld(now: Date, toleranceSeconds: number) { - return (now?.getTime() - (this.date?.getTime() || 0)) > (this.expectedEverySeconds + toleranceSeconds) * 1000; - } - -} diff --git a/src/main/angular/src/app/series/SeriesPoint.ts b/src/main/angular/src/app/series/SeriesPoint.ts deleted file mode 100644 index 5474e15..0000000 --- a/src/main/angular/src/app/series/SeriesPoint.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {Series} from "./Series"; -import {mapNotNull, validateBoolean, validateNumber} from "../COMMON"; -import {Value} from '../common/Value'; - -export class SeriesPoint { - - readonly min: Value; - - readonly avg: Value; - - readonly max: Value; - - readonly delta: Value; - - constructor( - readonly series: Series, - min: number | null, - avg: number | null, - max: number | null, - delta: number | null, - readonly state: boolean | null, - ) { - this.min = new Value(min, series.decimals); - this.avg = new Value(avg, series.decimals); - this.max = new Value(max, series.decimals); - this.delta = new Value(delta, series.decimals); - } - - static fromJson(json: any): SeriesPoint { - return new SeriesPoint( - Series.fromJson(json), - mapNotNull(json.min, validateNumber), - mapNotNull(json.avg, validateNumber), - mapNotNull(json.max, validateNumber), - mapNotNull(json.delta, validateNumber), - mapNotNull(json.state, validateBoolean), - ) - } - -} diff --git a/src/main/angular/src/app/series/SeriesSorter.ts b/src/main/angular/src/app/series/SeriesSorter.ts deleted file mode 100644 index 718f491..0000000 --- a/src/main/angular/src/app/series/SeriesSorter.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {Series} from "./Series"; -import {Sorter} from '../common/sorter/Sorter'; -import {Column} from '../common/sorter/Column'; -import {Order} from '../common/sorter/Order'; -import {Direction} from '../common/sorter/Direction'; -import {compareDates, compareNullable, compareNumbers, compareStrings, validateList, validateString} from '../COMMON'; - -export class SeriesSorter extends Sorter { - - static readonly nam: Column = Column.create("name", "Name", s => s.name, compareStrings); - - static readonly icon: Column = Column.create("icon", "Icon", s => s.name, compareStrings); - - static readonly type: Column = Column.create("delta", "Type", s => s.type, compareStrings); - - static readonly unit: Column = Column.create("unit", "Einheit", s => s.unit, compareStrings); - - static readonly date: Column = Column.create("first", "Zuerst", s => s.date, compareNullable(compareDates)); - - static readonly decimals: Column = Column.create("decimals", "Stellen", s => s.decimals, compareNumbers); - - static readonly value: Column = Column.create("value", "Wert", s => s.value.value, compareNullable(compareNumbers)); - - static COLUMNS: Column[] = [this.nam, this.icon, this.type, this.unit, this.date, this.decimals, this.value]; - - private static getColumn(name: string): Column { - return SeriesSorter.COLUMNS.filter(c => c.name === name)[0]; - } - - static fromJson(json: any): SeriesSorter { - return new SeriesSorter( - validateList(json, j => new Order( - SeriesSorter.getColumn(j.property), - validateString(j.direction) as Direction, - )) - ); - } - - constructor( - orders: Order[] = [], - ) { - super(orders); - } - -} diff --git a/src/main/angular/src/app/series/SeriesType.ts b/src/main/angular/src/app/series/SeriesType.ts deleted file mode 100644 index 2533340..0000000 --- a/src/main/angular/src/app/series/SeriesType.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum SeriesType { - BOOL = "BOOL", - DELTA = "DELTA", - VARYING = "VARYING", -} diff --git a/src/main/angular/src/app/series/bool/Bool.ts b/src/main/angular/src/app/series/bool/Bool.ts deleted file mode 100644 index a14b8a2..0000000 --- a/src/main/angular/src/app/series/bool/Bool.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Series} from "../Series"; -import {validateBoolean, validateDate} from "../../COMMON"; - -export class Bool { - - constructor( - readonly series: Series, - readonly date: Date, - readonly end: Date, - readonly state: boolean, - readonly terminated: boolean, - ) { - // - } - - static fromJson(json: any): Bool { - return new Bool( - Series.fromJson(json.series), - validateDate(json.date), - validateDate(json.end), - validateBoolean(json.state), - validateBoolean(json.terminated), - ); - } - -} diff --git a/src/main/angular/src/app/series/bool/bool-service.ts b/src/main/angular/src/app/series/bool/bool-service.ts deleted file mode 100644 index 309a0ca..0000000 --- a/src/main/angular/src/app/series/bool/bool-service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Injectable} from '@angular/core'; -import {ApiService, CrudService} from '../../COMMON'; -import {Bool} from './Bool'; - -@Injectable({ - providedIn: 'root' -}) -export class BoolService extends CrudService { - - constructor( - api: ApiService, - ) { - super(api, ["Bool"], Bool.fromJson); - } - -} diff --git a/src/main/angular/src/app/series/delta/Delta.ts b/src/main/angular/src/app/series/delta/Delta.ts deleted file mode 100644 index 629f191..0000000 --- a/src/main/angular/src/app/series/delta/Delta.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {validateDate, validateNumber} from "../../COMMON"; -import {Meter} from './meter/Meter'; - -export class Delta { - - constructor( - readonly meter: Meter, - readonly date: Date, - readonly first: number, - readonly last: number, - ) { - // - } - - static fromJson(json: any): Delta { - return new Delta( - Meter.fromJson(json.meter), - validateDate(json.date), - validateNumber(json.first), - validateNumber(json.last), - ); - } - -} diff --git a/src/main/angular/src/app/series/delta/delta-service.ts b/src/main/angular/src/app/series/delta/delta-service.ts deleted file mode 100644 index 9ef4736..0000000 --- a/src/main/angular/src/app/series/delta/delta-service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Injectable} from '@angular/core'; -import {ApiService, CrudService} from '../../COMMON'; -import {Delta} from './Delta'; - -@Injectable({ - providedIn: 'root' -}) -export class DeltaService extends CrudService { - - constructor( - api: ApiService, - ) { - super(api, ["Delta"], Delta.fromJson); - } - -} diff --git a/src/main/angular/src/app/series/delta/meter/Meter.ts b/src/main/angular/src/app/series/delta/meter/Meter.ts deleted file mode 100644 index bba5d5b..0000000 --- a/src/main/angular/src/app/series/delta/meter/Meter.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Series} from "../../Series"; -import {validateDate, validateNumber, validateString} from "../../../COMMON"; - -export class Meter { - - constructor( - readonly id: number, - readonly series: Series, - readonly number: string, - readonly first: Date, - ) { - // - } - - static fromJson(json: any): Meter { - return new Meter( - validateNumber(json.id), - Series.fromJson(json.series), - validateString(json.number), - validateDate(json.first), - ); - } - -} diff --git a/src/main/angular/src/app/series/list/series-list.component.html b/src/main/angular/src/app/series/list/series-list.component.html deleted file mode 100644 index d9c5c6f..0000000 --- a/src/main/angular/src/app/series/list/series-list.component.html +++ /dev/null @@ -1,83 +0,0 @@ -
-
Live
-
5 Min.
-
Stunde
-
Tag
-
Woche
-
Monat
-
Jahr
-
- -
-
-7
-
-6
-
-5
-
-4
-
-3
-
-2
-
-1
-
0
-
- - - - - - - - - - - - - @if (config.seriesList.interval === null) { - @for (series of seriesFiltered; track series.id) { - - - - @if (series.value.showDot) { - - } @else { - - } - - - @switch (series.type) { - @case (SeriesType.VARYING) { - - } - @case (SeriesType.DELTA) { - - } - @case (SeriesType.BOOL) { - - } - } - - } - } - - @if (config.seriesList.interval !== null) { - @for (seriesPoint of aggregateFiltered; track seriesPoint.series.id) { - - - - @if (seriesPoint.avg.showDot) { - - } @else { - - } - - - - - } - } - -
{{ series.name }}{{ series.value.integer }},{{ series.value.fraction }}{{ series.unit }}ΔB
{{ seriesPoint.series.name }}{{ seriesPoint.avg.integer }},{{ seriesPoint.avg.fraction }}{{ seriesPoint.series.unit }}Δ
- -@if (config.seriesList.interval === null) { -
{{ seriesFiltered.length }} / {{ seriesUnfiltered.length }}
-} @else { -
{{ aggregateFiltered.length }} / {{ aggregateUnfiltered.length }}
-} diff --git a/src/main/angular/src/app/series/list/series-list.component.less b/src/main/angular/src/app/series/list/series-list.component.less deleted file mode 100644 index e1b315f..0000000 --- a/src/main/angular/src/app/series/list/series-list.component.less +++ /dev/null @@ -1,93 +0,0 @@ -@import "../../../config"; - -.ChoiceList { - display: flex; - user-select: none; - border-left: @border solid gray; - border-right: @border solid gray; - border-bottom: @border solid gray; - - .Choice { - flex: 1; - padding: @space; - border-left: @border solid gray; - border-left: @border solid gray; - text-align: center; - } - - .Choice:first-child { - border: none; - } - - .ChoiceActive { - background-color: lightskyblue; - } - -} - -.ChoiceListInactive { - opacity: 20%; -} - -table { - width: 100%; - white-space: nowrap; - - th { - user-select: none; - } - - td, th { - text-align: center; - width: 0; - padding: @space calc(2 * @space); - } - - tr.Series:nth-child(even) { - background-color: #fff6e9; - } - - tr.Series:nth-child(odd) { - background-color: #fffce9; - } - - .name { - text-align: left; - width: 100%; - } - - .value { - text-align: right; - padding-right: 0; - } - - .valueInteger { - padding-right: 0; - text-align: right; - } - - .valueDot { - padding-left: 0; - padding-right: 0; - } - - .valueFraction { - padding-left: 0; - padding-right: 0; - text-align: left; - } - - .unit { - padding-left: @space; - text-align: left; - } - - .SeriesOld { - color: gray; - } - -} - -.Counts { - text-align: right; -} diff --git a/src/main/angular/src/app/series/list/series-list.component.ts b/src/main/angular/src/app/series/list/series-list.component.ts deleted file mode 100644 index f1b4aae..0000000 --- a/src/main/angular/src/app/series/list/series-list.component.ts +++ /dev/null @@ -1,155 +0,0 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {Config} from '../../config/Config'; -import {Subscription, timer} from 'rxjs'; -import {ConfigService} from '../../config/config.service'; -import {SeriesService} from '../series.service'; -import {Series} from '../Series'; -import {ColumnComponent} from '../../common/column/column.component'; - -import {SeriesSorter} from '../SeriesSorter'; -import {FormsModule} from '@angular/forms'; -import {Interval} from '../Interval'; -import {SeriesType} from '../SeriesType'; -import {AllSeriesPointResponse, AllSeriesPointResponseEntry} from '../AllSeriesPointResponse'; -import {Varying} from '../varying/Varying'; - -@Component({ - selector: 'app-series-list', - imports: [ - ColumnComponent, - FormsModule - ], - templateUrl: './series-list.component.html', - styleUrl: './series-list.component.less' -}) -export class SeriesListComponent implements OnInit, OnDestroy { - - protected readonly Series = Series; - - protected readonly Interval = Interval; - - protected readonly SeriesType = SeriesType; - - protected readonly SeriesSorter = SeriesSorter; - - protected now: Date = new Date(); - - protected seriesUnfiltered: Series[] = []; - - protected seriesFiltered: Series[] = []; - - protected aggregateResponse: AllSeriesPointResponse | null = null; - - protected aggregateUnfiltered: AllSeriesPointResponseEntry[] = []; - - protected aggregateFiltered: AllSeriesPointResponseEntry[] = []; - - protected config: Config = new Config(); - - private readonly subs: Subscription[] = []; - - private saveConfigTimeout: any = undefined; - - constructor( - readonly configService: ConfigService, - readonly seriesService: SeriesService, - ) { - // - } - - ngOnInit(): void { - this.configService.get(config => { - this.config = config; - this.fetchList(); - this.subs.push(this.seriesService.subscribe(this.updateSeries)); - this.subs.push(timer(1000, 1000).subscribe(() => this.now = new Date())); - }); - } - - ngOnDestroy(): void { - this.subs.forEach(sub => sub.unsubscribe()); - } - - private readonly updateSeries = (series: Series) => { - const index = this.seriesUnfiltered.findIndex(t => t.id === series.id); - if (index >= 0) { - this.seriesUnfiltered.splice(index, 1, series); - } else { - this.seriesUnfiltered.push(series); - } - this.applySeriesFilter(); - }; - - saveConfig(fetch: boolean) { - if (this.saveConfigTimeout) { - clearTimeout(this.saveConfigTimeout); - this.saveConfigTimeout = undefined; - } - if (fetch) { - this.fetchList(); - } else { - this.applySeriesFilter(); - this.applyAggregateFilter(); - } - this.saveConfigTimeout = setTimeout(() => this.configService.set(this.config, config => this.config = config), 700); - } - - private readonly filter = (series: Series) => { - const words = this.config.seriesList.search - .replaceAll(/([0-9])([a-zA-Z])/g, '$1 $2') - .replaceAll(/([a-z])([A-Z0-9])/g, '$1 $2') - .replaceAll(/([A-Z])([0-9])/g, '$1 $2') - .toLocaleLowerCase() - .trim() - .split(/\W+/); - return words.every(word => - series.name.toLocaleLowerCase().includes(word) - || series.unit.toLocaleLowerCase().includes(word) - || series.value.integer.includes(word) - || series.name.toLocaleLowerCase().includes(word) - || (series.type === SeriesType.DELTA && "delta difference".includes(word)) - || (series.type === SeriesType.VARYING && "average avg minimum maximum".includes(word)) - || (series.type === SeriesType.BOOL && "boolean".includes(word)) - ); - } - - protected setInterval(interval: Interval | null): void { - if (this.config.seriesList.interval !== interval) { - this.config.seriesList.interval = interval; - this.saveConfig(true); - } - } - - protected setOffset(offset: number): void { - if (this.config.seriesList.offset !== offset) { - this.config.seriesList.offset = offset; - this.saveConfig(true); - } - } - - private fetchList() { - if (this.config.seriesList.interval === null) { - this.seriesService.findAll(list => { - this.seriesUnfiltered = list; - this.applySeriesFilter(); - }); - } else { - this.seriesService.allSeriesPoint(this.config.seriesList.interval, this.config.seriesList.offset, response => { - this.aggregateResponse = response; - this.aggregateUnfiltered = response.seriesPoints; - this.applyAggregateFilter(); - }); - } - } - - private applySeriesFilter() { - this.seriesFiltered = this.seriesUnfiltered.filter(this.filter).sort(this.config.seriesList.sorter.compare); - } - - private applyAggregateFilter() { - this.aggregateUnfiltered = this.aggregateResponse?.seriesPoints || []; - this.aggregateFiltered = this.aggregateUnfiltered.filter(a => this.filter(a.series)).sort((a, b) => this.config.seriesList.sorter.compare(a.series, b.series)); - } - - protected readonly Varying = Varying; -} diff --git a/src/main/angular/src/app/series/series.service.ts b/src/main/angular/src/app/series/series.service.ts deleted file mode 100644 index e65ce36..0000000 --- a/src/main/angular/src/app/series/series.service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {Injectable} from '@angular/core'; -import {ApiService, EntityListService, Next, validateNumber} from "../COMMON"; -import {Series} from './Series'; -import {Interval} from './Interval'; -import {AllSeriesPointResponse} from './AllSeriesPointResponse'; -import {AllSeriesPointRequest} from './AllSeriesPointRequest'; -import {Graph} from "../plot/axis/graph/Graph"; - -@Injectable({ - providedIn: 'root', -}) -export class SeriesService extends EntityListService { - - constructor( - api: ApiService, - ) { - super(api, ['Series'], Series.fromJson, Series.equals, Series.compareName); - } - - oneSeriesPoints(graph: Graph, next: Next): void { - const request = { - id: graph.series.id, - factor: graph.factor, - operation: graph.operation, - id2: graph.series2?.id, - factor2: graph.factor2, - interval: graph.axis.plot.interval.name, - offset: graph.axis.plot.offset, - duration: graph.axis.plot.duration, - }; - this.api.postList([...this.path, 'oneSeriesPoints'], request, (outer: any[]) => outer.map(validateNumber), next); - } - - allSeriesPoint(interval: Interval, offset: number, next: (response: AllSeriesPointResponse) => void) { - const request = new AllSeriesPointRequest(interval, offset); - this.api.postSingle([...this.path, 'allSeriesPoint'], request.toJson(), AllSeriesPointResponse.fromJson, next); - } - -} diff --git a/src/main/angular/src/app/series/varying/Varying.ts b/src/main/angular/src/app/series/varying/Varying.ts deleted file mode 100644 index 2125b8d..0000000 --- a/src/main/angular/src/app/series/varying/Varying.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Series} from "../Series"; -import {validateDate, validateNumber} from "../../COMMON"; - -export class Varying { - - constructor( - readonly series: Series, - readonly date: Date, - readonly min: number, - readonly max: number, - readonly avg: number, - ) { - // - } - - static fromJson(json: any): Varying { - return new Varying( - Series.fromJson(json.series), - validateDate(json.date), - validateNumber(json.min), - validateNumber(json.max), - validateNumber(json.avg), - ); - } - -} diff --git a/src/main/angular/src/app/series/varying/varying-service.ts b/src/main/angular/src/app/series/varying/varying-service.ts deleted file mode 100644 index 4f9f4bc..0000000 --- a/src/main/angular/src/app/series/varying/varying-service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Injectable} from '@angular/core'; -import {ApiService, CrudService} from '../../COMMON'; -import {Varying} from './Varying'; - -@Injectable({ - providedIn: 'root' -}) -export class VaryingService extends CrudService { - - constructor( - api: ApiService, - ) { - super(api, ["Varying"], Varying.fromJson); - } - -} diff --git a/src/main/angular/src/app/topic/TimestampType.ts b/src/main/angular/src/app/topic/TimestampType.ts deleted file mode 100644 index d6bdf25..0000000 --- a/src/main/angular/src/app/topic/TimestampType.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum TimestampType { - EPOCH_MILLISECONDS = "EPOCH_MILLISECONDS", - EPOCH_SECONDS = "EPOCH_SECONDS", -} diff --git a/src/main/angular/src/app/topic/Topic.ts b/src/main/angular/src/app/topic/Topic.ts deleted file mode 100644 index 0b8c0a1..0000000 --- a/src/main/angular/src/app/topic/Topic.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {TopicQuery} from "./TopicQuery"; - -import {TimestampType} from './TimestampType'; -import {mapNotNull, validateBoolean, validateDate, validateList, validateNumber, validateString} from '../COMMON'; - -export class Topic { - - readonly used: boolean; - - constructor( - readonly id: number, - readonly name: string, - readonly first: Date, - readonly last: Date, - readonly count: number, - readonly enabled: boolean, - readonly timestampType: TimestampType, - readonly timestampQuery: string, - readonly timestampLast: Date | null, - readonly meterNumberQuery: string, - readonly meterNumberLast: string, - readonly queries: TopicQuery[], - readonly payload: string, - readonly error: string, - ) { - this.used = enabled && timestampQuery.length > 0 && queries.some(q => q.series && q.valueQuery); - } - - static fromJson(json: any): Topic { - return new Topic( - validateNumber(json.id), - validateString(json.name), - validateDate(json.first), - validateDate(json.last), - validateNumber(json.count), - validateBoolean(json.enabled), - validateString(json.timestampType) as TimestampType, - validateString(json.timestampQuery), - mapNotNull(json.timestampLast, validateDate), - validateString(json.meterNumberQuery), - validateString(json.meterNumberLast), - validateList(json.queries, TopicQuery.fromJson), - validateString(json.payload), - validateString(json.error), - ); - } - -} diff --git a/src/main/angular/src/app/topic/TopicQuery.ts b/src/main/angular/src/app/topic/TopicQuery.ts deleted file mode 100644 index 17f860a..0000000 --- a/src/main/angular/src/app/topic/TopicQuery.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {TopicQueryFunction} from './TopicQueryFunction'; -import {Series} from '../series/Series'; -import {validateString} from '../COMMON'; - -export class TopicQuery { - // series - // valueQuery - // beginQuery - // terminatedQuery - // function - // factor - constructor( - readonly series: Series, - readonly valueQuery: string, - readonly func: TopicQueryFunction, - ) { - // - } - - static fromJson(json: any): TopicQuery { - return new TopicQuery( - Series.fromJson(json.series), - validateString(json.valueQuery), - validateString(json.function) as TopicQueryFunction, - ); - } - -} diff --git a/src/main/angular/src/app/topic/TopicQueryFunction.ts b/src/main/angular/src/app/topic/TopicQueryFunction.ts deleted file mode 100644 index cb1ad8d..0000000 --- a/src/main/angular/src/app/topic/TopicQueryFunction.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum TopicQueryFunction { - NONE = "NONE", - ONLY_POSITIVE = "ONLY_POSITIVE", - ONLY_NEGATIVE_BUT_NEGATE = "ONLY_NEGATIVE_BUT_NEGATE", -} diff --git a/src/main/angular/src/app/topic/list/topic-list.component.html b/src/main/angular/src/app/topic/list/topic-list.component.html deleted file mode 100644 index 68a6f39..0000000 --- a/src/main/angular/src/app/topic/list/topic-list.component.html +++ /dev/null @@ -1,46 +0,0 @@ -
- - -
- - - @for (topic of sorted(); track topic.id) { - - - - - - - - - - - @if (topic.queries[0]?.series?.type === SeriesType.DELTA) { - - - - - - - } - @for (query of topic.queries; track $index) { - - - - - - @if (query.series.value.showDot) { - - } @else { - - } - - - - - } - - - - } -
{{ topic.name }}{{ topic.last | date:'long':'':'de-DE' }}{{ topic.payload }}
{{ topic.timestampQuery }}{{ topic.timestampType }}{{ topic.timestampLast | date:'long':'':'de-DE' }}
{{ topic.meterNumberQuery }}{{ topic.meterNumberLast }}{{ topic.timestampLast | date:'long':'':'de-DE' }}
{{ query.valueQuery }}{{ query.func }}{{ query.series.name }}{{ query.series.value.integer }},{{ query.series.value.fraction }}{{ query.series.unit }}{{ query.series.date | date:'long':'':'de-DE' }}
 
diff --git a/src/main/angular/src/app/topic/list/topic-list.component.less b/src/main/angular/src/app/topic/list/topic-list.component.less deleted file mode 100644 index 5f528fd..0000000 --- a/src/main/angular/src/app/topic/list/topic-list.component.less +++ /dev/null @@ -1,112 +0,0 @@ -@import "../../../config"; - -table.TopicList { - - tr { - th, td { - border: 1px solid gray; - white-space: nowrap; - vertical-align: top; - } - } - - tr.head { - background-color: lightgray; - } - - tr.query { - background-color: lightskyblue; - } - - tr.timestamp { - background-color: darkseagreen; - } - - tr.meter { - background-color: #ffe7bd; - } - - tr.spacer { - th, td { - border: none; - } - } - - .empty { - background-color: #ffd3d3; - } - - .name { - font-weight: bold; - } - - .last { - - } - - .payload { - white-space: unset !important; - } - - .timestampType { - - } - - .timestampQuery { - - } - - .timestampLast { - text-align: right; - } - - .meterNumberLast { - text-align: right; - } - - .valueQuery { - - } - - .valueFunction { - - } - - .valueSeries { - text-align: right; - } - - .valueSeriesValueInteger { - border-right: none !important; - padding-right: 0; - text-align: right; - width: 0; - } - - .valueSeriesValueDot { - border-right: none !important; - padding-right: 0; - border-left: none !important; - padding-left: 0; - width: 0; - } - - .valueSeriesValueFraction { - border-right: none !important; - padding-right: 0; - border-left: none !important; - padding-left: 0; - width: 0; - } - - .valueSeriesValueUnit { - border-left: none !important; - text-align: left; - width: 0; - } - - .valueSeriesDate { - width: 0; - } - -} diff --git a/src/main/angular/src/app/topic/list/topic-list.component.ts b/src/main/angular/src/app/topic/list/topic-list.component.ts deleted file mode 100644 index ce4aafd..0000000 --- a/src/main/angular/src/app/topic/list/topic-list.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {DatePipe} from '@angular/common'; -import {Topic} from '../Topic'; -import {TopicService} from '../topic.service'; -import {FormsModule} from '@angular/forms'; -import {Subscription, timer} from 'rxjs'; -import {TimestampType} from '../TimestampType'; -import {TopicQueryFunction} from '../TopicQueryFunction'; -import {Series} from '../../series/Series'; -import {SeriesService} from '../../series/series.service'; -import {Config} from '../../config/Config'; -import {ageString} from '../../COMMON'; -import {ConfigService} from '../../config/config.service'; -import {SeriesType} from '../../series/SeriesType'; - -@Component({ - selector: 'app-topic-list', - imports: [ - FormsModule, - DatePipe - ], - templateUrl: './topic-list.component.html', - styleUrl: './topic-list.component.less' -}) -export class TopicListComponent implements OnInit, OnDestroy { - - protected readonly ageString = ageString; - - protected readonly SeriesType = SeriesType; - - protected now: Date = new Date(); - - protected topicList: Topic[] = []; - - protected seriesList: Series[] = []; - - protected config: Config = new Config(); - - private readonly subs: Subscription[] = []; - - constructor( - readonly configService: ConfigService, - readonly seriesService: SeriesService, - readonly topicService: TopicService, - ) { - // - } - - ngOnInit(): void { - this.configService.get(config => this.config = config); - this.seriesService.findAll(list => this.seriesList = list.sort((a, b) => a.name.localeCompare(b.name))); - this.topicService.findAll(list => this.topicList = list); - this.subs.push(this.topicService.subscribe(this.update)); - this.subs.push(timer(1000, 1000).subscribe(() => this.now = new Date())); - } - - ngOnDestroy(): void { - this.subs.forEach(sub => sub.unsubscribe()); - } - - private readonly update = (topic: Topic) => { - const index = this.topicList.findIndex(t => t.id === topic.id); - if (index >= 0) { - this.topicList.splice(index, 1, topic); - } else { - this.topicList.push(topic); - } - }; - - sorted() { - return this.topicList.filter(this.filter).sort(this.compare); - } - - private readonly filter = (topic: Topic) => { - return ((!topic.used && this.config.topicList.unused) || (topic.used && this.config.topicList.used)); - } - - private readonly compare = (a: Topic, b: Topic) => { - return a.name.localeCompare(b.name); - } - - TimestampTypes() { - return Object.values(TimestampType); - } - - TopicQueryFunctions() { - return Object.values(TopicQueryFunction); - } - - configSet() { - this.configService.set(this.config, config => this.config = config); - } - -} diff --git a/src/main/angular/src/app/topic/topic.service.ts b/src/main/angular/src/app/topic/topic.service.ts deleted file mode 100644 index 8ac6d60..0000000 --- a/src/main/angular/src/app/topic/topic.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Injectable} from '@angular/core'; -import {Topic} from './Topic'; -import {ApiService, CrudService} from '../COMMON'; - -@Injectable({ - providedIn: 'root' -}) -export class TopicService extends CrudService { - - constructor( - api: ApiService, - ) { - super(api, ["Topic"], Topic.fromJson); - } - -} diff --git a/src/main/angular/src/app/weather/WeatherHour.ts b/src/main/angular/src/app/weather/WeatherHour.ts deleted file mode 100644 index 67e7f87..0000000 --- a/src/main/angular/src/app/weather/WeatherHour.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {validateDate, validateNumber} from '../COMMON'; - -export class WeatherHour { - - constructor( - readonly date: Date, - readonly clouds: number, - readonly irradiation: number, - readonly precipitation: number, - readonly temperature: number, - ) { - // - } - - static fromJson(json: any): WeatherHour { - return new WeatherHour( - validateDate(json.date), - validateNumber(json.clouds), - validateNumber(json.irradiation), - validateNumber(json.precipitation), - validateNumber(json.temperature), - ); - } - -} diff --git a/src/main/angular/src/app/weather/plot/weather-component.html b/src/main/angular/src/app/weather/plot/weather-component.html deleted file mode 100644 index 4585a0f..0000000 --- a/src/main/angular/src/app/weather/plot/weather-component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/src/main/angular/src/app/weather/plot/weather-component.less b/src/main/angular/src/app/weather/plot/weather-component.less deleted file mode 100644 index cbe226c..0000000 --- a/src/main/angular/src/app/weather/plot/weather-component.less +++ /dev/null @@ -1,4 +0,0 @@ -.container { - width: 100%; - height: 100%; -} diff --git a/src/main/angular/src/app/weather/plot/weather-component.ts b/src/main/angular/src/app/weather/plot/weather-component.ts deleted file mode 100644 index ec8df5e..0000000 --- a/src/main/angular/src/app/weather/plot/weather-component.ts +++ /dev/null @@ -1,204 +0,0 @@ -import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core'; -import {WeatherService} from '../weather-service'; -import {WeatherHour} from '../WeatherHour'; -import {Chart} from 'chart.js'; - -import {de} from 'date-fns/locale'; -import {format} from 'date-fns'; - -export function toPoint(f: (hour: WeatherHour) => number): (hour: WeatherHour) => { x: number, y: number } { - return (hour: WeatherHour) => { - return { - x: hour.date.getTime(), - y: f(hour), - }; - }; -} - -const TEMPERATURE_HIGH_COLOR = '#FF0000'; -const TEMPERATURE_LOW_COLOR = '#0000FF'; -const TEMPERATURE_LOW_THRESHOLD = 0; -const PRECIPITATION_COLOR = '#0000FF'; -const CLOUDS_COLOR = '#cccccc'; -const SUN_COLOR = '#ffc400'; - -@Component({ - selector: 'app-weather-component', - imports: [], - templateUrl: './weather-component.html', - styleUrl: './weather-component.less' -}) -export class WeatherComponent implements AfterViewInit { - - @ViewChild('chartCanvas') - protected canvasRef!: ElementRef; - - @ViewChild('container') - protected chartContainer!: ElementRef; - - private chart!: Chart; - - constructor( - readonly weatherService: WeatherService, - ) { - // - } - - ngAfterViewInit(): void { - this.chart = new Chart(this.canvasRef.nativeElement, { - type: 'line', - data: { - datasets: [], - }, - options: { - responsive: true, - maintainAspectRatio: false, - animation: false, - plugins: { - legend: { - display: true, - }, - tooltip: { - position: 'nearest', - mode: 'index', - usePointStyle: true, - callbacks: { - title: function (items) { - const date = items[0].parsed.x as unknown as Date; - return format(date, 'EE dd.MM. HH:mm', {locale: de}); - }, - }, - }, - title: { - display: true, - text: "Wetter", - } - }, - scales: { - x: { - type: 'time', - time: { - unit: "day", - displayFormats: { - day: "EE dd.MM" - } - }, - adapters: { - date: { - locale: de, - } - }, - }, - y_temperature: { - position: "right", - title: { - display: true, - text: "Temperatur [°C]", - color: TEMPERATURE_HIGH_COLOR, - }, - ticks: { - color: TEMPERATURE_HIGH_COLOR, - }, - min: 0, - max: 35, - }, - y_precipitation: { - title: { - display: true, - text: "Niederschlag [mm]", - color: PRECIPITATION_COLOR, - }, - ticks: { - color: PRECIPITATION_COLOR, - }, - min: 0, - max: 15, - }, - y_sun: { - position: "right", - title: { - display: true, - text: "Sonne [kWh/m²]", - color: SUN_COLOR, - }, - ticks: { - color: SUN_COLOR, - }, - min: 0, - max: 1000, - }, - y_clouds: { - display: false, - title: { - display: true, - text: "Wolkenbedeckung [%]", - color: CLOUDS_COLOR, - }, - ticks: { - color: CLOUDS_COLOR, - }, - min: 0, - max: 100, - }, - }, - }, - }); - this.weatherService.all(hours => { - const now = Date.now(); - const filtered = hours.filter(h => h.date.getTime() >= now); - this.chart.data.datasets.push({ - label: "Niederschlag", - type: "bar", - yAxisID: "y_precipitation", - data: filtered.map(toPoint(h => h.precipitation)), - borderColor: PRECIPITATION_COLOR, - backgroundColor: PRECIPITATION_COLOR + '66', - borderWidth: 0, - pointStyle: "rect", - }); - this.chart.data.datasets.push({ - label: "Sonne", - type: "line", - fill: "origin", - yAxisID: "y_sun", - data: filtered.map(toPoint(h => h.irradiation)), - borderColor: SUN_COLOR, - backgroundColor: SUN_COLOR + '88', - borderWidth: 0, - pointRadius: 0, - }); - this.chart.data.datasets.push({ - label: "Temperatur", - type: "line", - yAxisID: "y_temperature", - data: filtered.map(toPoint(h => h.temperature)), - borderColor: function (context) { - const value = context.dataset.data[context.dataIndex]; - return value !== null && typeof value === 'object' && value.hasOwnProperty("y") && value.y >= TEMPERATURE_LOW_THRESHOLD ? TEMPERATURE_HIGH_COLOR : TEMPERATURE_LOW_COLOR; - }, - backgroundColor: TEMPERATURE_HIGH_COLOR + '66', - fill: { - target: {value: TEMPERATURE_LOW_THRESHOLD}, - above: TEMPERATURE_HIGH_COLOR + '66', - below: TEMPERATURE_LOW_COLOR + '66', - }, - borderWidth: 0, - pointRadius: 5, - pointStyle: "cross", - }); - this.chart.data.datasets.push({ - label: "Wolken", - type: "line", - fill: "origin", - yAxisID: "y_clouds", - data: filtered.map(toPoint(h => h.clouds)), - borderColor: CLOUDS_COLOR, - backgroundColor: CLOUDS_COLOR + '88', - borderWidth: 0, - pointRadius: 0, - }); - this.chart.update(); - }); - } - -} diff --git a/src/main/angular/src/app/weather/weather-service.ts b/src/main/angular/src/app/weather/weather-service.ts deleted file mode 100644 index be77f96..0000000 --- a/src/main/angular/src/app/weather/weather-service.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {Injectable} from '@angular/core'; -import {ApiService, CrudService, Next} from '../COMMON'; -import {WeatherHour} from './WeatherHour'; - -@Injectable({ - providedIn: 'root' -}) -export class WeatherService extends CrudService { - - constructor( - api: ApiService, - ) { - super(api, ['Weather'], WeatherHour.fromJson); - } - - all(next: Next) { - this.getList(['all'], next); - } - -} diff --git a/src/main/angular/src/config.less b/src/main/angular/src/config.less deleted file mode 100644 index ff86788..0000000 --- a/src/main/angular/src/config.less +++ /dev/null @@ -1,2 +0,0 @@ -@space: 0.25em; -@border: 0.01em; diff --git a/src/main/angular/src/index.html b/src/main/angular/src/index.html index 0a234f8..d83079f 100644 --- a/src/main/angular/src/index.html +++ b/src/main/angular/src/index.html @@ -1,11 +1,11 @@ - - + Angular + diff --git a/src/main/angular/src/main.ts b/src/main/angular/src/main.ts index 9c2613b..5df75f9 100644 --- a/src/main/angular/src/main.ts +++ b/src/main/angular/src/main.ts @@ -1,6 +1,6 @@ -import {bootstrapApplication} from '@angular/platform-browser'; -import {appConfig} from './app/app.config'; -import {App} from './app/app'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; bootstrapApplication(App, appConfig) .catch((err) => console.error(err)); diff --git a/src/main/angular/src/styles.less b/src/main/angular/src/styles.less index 704217f..90d4ee0 100644 --- a/src/main/angular/src/styles.less +++ b/src/main/angular/src/styles.less @@ -1,51 +1 @@ -html, body { - height: 100%; - margin: 0; -} - -body { - font-family: sans-serif; -} - -table { - width: 100%; - border-collapse: collapse; - - td, th { - padding: 0.25em; - vertical-align: bottom; - } -} - -input, select, textarea { - font-family: inherit; - font-size: inherit; -} - -input:not([type=checkbox]), select { - width: 100%; - margin: 0; - outline: none; - background-color: rgba(255, 255, 255, 0.8); - border: 1px solid black; - padding: 0.125em; -} - -.button { - margin: 0; - border: 1px solid gray; - border-radius: 0.25em; - box-shadow: 0 0 0.25em black; -} - -.buttonAdd { - background-color: lightgreen; -} - -.buttonCopy { - background-color: lightskyblue; -} - -.buttonRemove { - background-color: indianred; -} +/* You can add global styles to this file, and also import other style files */