Merge pull request #48 from dawnsystem/copilot/add-ai-suggestions-panel

Add AI suggestions panel to document detail page
This commit is contained in:
dawnsystem 2025-11-15 16:14:34 +01:00 committed by GitHub
commit 0dc1061f50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1336 additions and 52 deletions

View file

@ -11,6 +11,7 @@
},
"private": true,
"dependencies": {
"@angular/animations": "~20.3.12",
"@angular/cdk": "^20.2.6",
"@angular/common": "~20.3.2",
"@angular/compiler": "~20.3.2",

120
src-ui/pnpm-lock.yaml generated
View file

@ -8,6 +8,9 @@ importers:
.:
dependencies:
'@angular/animations':
specifier: ~20.3.12
version: 20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/cdk':
specifier: ^20.2.6
version: 20.2.6(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
@ -22,28 +25,28 @@ importers:
version: 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms':
specifier: ~20.3.2
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/localize':
specifier: ~20.3.2
version: 20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)
'@angular/platform-browser':
specifier: ~20.3.2
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
version: 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser-dynamic':
specifier: ~20.3.2
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
'@angular/router':
specifier: ~20.3.2
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
version: 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@ng-bootstrap/ng-bootstrap':
specifier: ^19.0.1
version: 19.0.1(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@popperjs/core@2.11.8)(rxjs@7.8.2)
version: 19.0.1(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@popperjs/core@2.11.8)(rxjs@7.8.2)
'@ng-select/ng-select':
specifier: ^20.6.3
version: 20.6.3(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))
version: 20.6.3(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))
'@ngneat/dirty-check-forms':
specifier: ^3.0.3
version: 3.0.3(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2)
version: 3.0.3(291c247a225ddc29ee470ed21e444e55)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@ -73,7 +76,7 @@ importers:
version: 10.1.0(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
ngx-ui-tour-ng-bootstrap:
specifier: ^17.0.1
version: 17.0.1(a51ec0d773a3e93ac3d51d20ca771021)
version: 17.0.1(f8db16ccbb0d6be45bab4b8410cc9846)
rxjs:
specifier: ^7.8.2
version: 7.8.2
@ -92,10 +95,10 @@ importers:
devDependencies:
'@angular-builders/custom-webpack':
specifier: ^20.0.0
version: 20.0.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)
version: 20.0.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)
'@angular-builders/jest':
specifier: ^20.0.0
version: 20.0.0(617e23274585616dcf62fd78c9140eac)
version: 20.0.0(496b29fc4599be2dae83ff2679fdbd16)
'@angular-devkit/core':
specifier: ^20.3.3
version: 20.3.3(chokidar@4.0.3)
@ -119,7 +122,7 @@ importers:
version: 20.3.0(eslint@9.36.0(jiti@1.21.7))(typescript@5.8.3)
'@angular/build':
specifier: ^20.3.3
version: 20.3.3(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)
version: 20.3.3(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)
'@angular/cli':
specifier: ~20.3.3
version: 20.3.3(@types/node@24.6.1)(chokidar@4.0.3)
@ -161,7 +164,7 @@ importers:
version: 16.0.0
jest-preset-angular:
specifier: ^15.0.2
version: 15.0.2(ccefccc315e3e4bd30d78eb49c90d46a)
version: 15.0.2(83827844341020d1e6edc9d0e74e3f3d)
jest-websocket-mock:
specifier: ^2.5.0
version: 2.5.0
@ -403,6 +406,12 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '*'
'@angular/animations@20.3.12':
resolution: {integrity: sha512-tkzruF0pbcOrC2lwsPKjkp5btazs6vcX4At7kyVFjjuPbgI6RNG+MoFXHpN9ypenscYtTAhDcPSmjBnzoDaXhQ==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
peerDependencies:
'@angular/core': 20.3.12
'@angular/build@20.0.4':
resolution: {integrity: sha512-SIYLg2st05Q5hgFrxwj6L4i9j2j2JNWYoYgacXp+mw9YVhFiC02Ymbakc9fq+3+sWlm0XTX5JgrupV2ac1ytNQ==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
@ -7096,13 +7105,13 @@ snapshots:
- chokidar
- typescript
'@angular-builders/custom-webpack@20.0.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)':
dependencies:
? '@angular-builders/custom-webpack@20.0.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)'
: dependencies:
'@angular-builders/common': 4.0.0(@types/node@24.6.1)(chokidar@4.0.3)(typescript@5.8.3)
'@angular-devkit/architect': 0.2000.4(chokidar@4.0.3)
'@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)
'@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)
'@angular-devkit/core': 20.3.3(chokidar@4.0.3)
'@angular/build': 20.3.3(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)
'@angular/build': 20.3.3(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)
'@angular/compiler-cli': 20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3)
lodash: 4.17.21
webpack-merge: 6.0.1
@ -7150,17 +7159,17 @@ snapshots:
- webpack-cli
- yaml
'@angular-builders/jest@20.0.0(617e23274585616dcf62fd78c9140eac)':
'@angular-builders/jest@20.0.0(496b29fc4599be2dae83ff2679fdbd16)':
dependencies:
'@angular-builders/common': 4.0.0(@types/node@24.6.1)(chokidar@4.0.3)(typescript@5.8.3)
'@angular-devkit/architect': 0.2000.4(chokidar@4.0.3)
'@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)
'@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)
'@angular-devkit/core': 20.3.3(chokidar@4.0.3)
'@angular/compiler-cli': 20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
jest: 30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3))
jest-preset-angular: 14.6.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(canvas@3.0.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3)
jest-preset-angular: 14.6.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(canvas@3.0.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3)
lodash: 4.17.21
transitivePeerDependencies:
- '@babel/core'
@ -7192,13 +7201,13 @@ snapshots:
transitivePeerDependencies:
- chokidar
'@angular-devkit/build-angular@20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)':
'@angular-devkit/build-angular@20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.5(@types/node@24.6.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)':
dependencies:
'@ampproject/remapping': 2.3.0
'@angular-devkit/architect': 0.2000.4(chokidar@4.0.3)
'@angular-devkit/build-webpack': 0.2000.4(chokidar@4.0.3)(webpack-dev-server@5.2.1(webpack@5.102.0))(webpack@5.99.8(esbuild@0.25.5))
'@angular-devkit/core': 20.0.4(chokidar@4.0.3)
'@angular/build': 20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)
'@angular/build': 20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)
'@angular/compiler-cli': 20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3)
'@babel/core': 7.27.1
'@babel/generator': 7.27.1
@ -7254,7 +7263,7 @@ snapshots:
optionalDependencies:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/localize': 20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
esbuild: 0.25.5
jest: 30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3))
jest-environment-jsdom: 30.2.0(canvas@3.0.0)
@ -7386,7 +7395,12 @@ snapshots:
eslint: 9.36.0(jiti@1.21.7)
typescript: 5.8.3
'@angular/build@20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)':
'@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))':
dependencies:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1
'@angular/build@20.0.4(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)':
dependencies:
'@ampproject/remapping': 2.3.0
'@angular-devkit/architect': 0.2000.4(chokidar@4.0.3)
@ -7421,7 +7435,7 @@ snapshots:
optionalDependencies:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/localize': 20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
less: 4.3.0
lmdb: 3.3.0
postcss: 8.5.3
@ -7438,7 +7452,7 @@ snapshots:
- tsx
- yaml
'@angular/build@20.3.3(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)':
'@angular/build@20.3.3(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.6.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)':
dependencies:
'@ampproject/remapping': 2.3.0
'@angular-devkit/architect': 0.2003.3(chokidar@4.0.3)
@ -7473,7 +7487,7 @@ snapshots:
optionalDependencies:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/localize': 20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
less: 4.3.0
lmdb: 3.4.2
postcss: 8.5.3
@ -7557,11 +7571,11 @@ snapshots:
'@angular/compiler': 20.3.2
zone.js: 0.15.1
'@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)':
'@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)':
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
rxjs: 7.8.2
tslib: 2.8.1
@ -7576,25 +7590,27 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@angular/platform-browser-dynamic@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))':
'@angular/platform-browser-dynamic@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))':
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/compiler': 20.3.2
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
tslib: 2.8.1
'@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))':
'@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))':
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1
optionalDependencies:
'@angular/animations': 20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)':
'@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)':
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
rxjs: 7.8.2
tslib: 2.8.1
@ -9403,28 +9419,28 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
'@ng-bootstrap/ng-bootstrap@19.0.1(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@popperjs/core@2.11.8)(rxjs@7.8.2)':
'@ng-bootstrap/ng-bootstrap@19.0.1(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@popperjs/core@2.11.8)(rxjs@7.8.2)':
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/forms': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/localize': 20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2)
'@popperjs/core': 2.11.8
rxjs: 7.8.2
tslib: 2.8.1
'@ng-select/ng-select@20.6.3(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))':
'@ng-select/ng-select@20.6.3(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))':
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/forms': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
tslib: 2.8.1
? '@ngneat/dirty-check-forms@3.0.3(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2)'
: dependencies:
'@ngneat/dirty-check-forms@3.0.3(291c247a225ddc29ee470ed21e444e55)':
dependencies:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/forms': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/router': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/forms': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/router': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
lodash-es: 4.17.21
rxjs: 7.8.2
tslib: 2.8.1
@ -12158,11 +12174,11 @@ snapshots:
optionalDependencies:
jest-resolve: 30.2.0
jest-preset-angular@14.6.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(canvas@3.0.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3):
jest-preset-angular@14.6.0(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(canvas@3.0.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3):
dependencies:
'@angular/compiler-cli': 20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
bs-logger: 0.2.6
esbuild-wasm: 0.25.10
jest: 30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3))
@ -12184,12 +12200,12 @@ snapshots:
- supports-color
- utf-8-validate
jest-preset-angular@15.0.2(ccefccc315e3e4bd30d78eb49c90d46a):
jest-preset-angular@15.0.2(83827844341020d1e6edc9d0e74e3f3d):
dependencies:
'@angular/compiler-cli': 20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
'@angular/platform-browser': 20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
'@jest/environment-jsdom-abstract': 30.2.0(canvas@3.0.0)(jsdom@26.1.0(canvas@3.0.0))
bs-logger: 0.2.6
esbuild-wasm: 0.25.10
@ -12883,20 +12899,20 @@ snapshots:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
tslib: 2.8.1
ngx-ui-tour-core@15.0.0(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2):
ngx-ui-tour-core@15.0.0(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2):
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/router': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
'@angular/router': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
rxjs: 7.8.2
tslib: 2.8.1
ngx-ui-tour-ng-bootstrap@17.0.1(a51ec0d773a3e93ac3d51d20ca771021):
ngx-ui-tour-ng-bootstrap@17.0.1(f8db16ccbb0d6be45bab4b8410cc9846):
dependencies:
'@angular/common': 20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@ng-bootstrap/ng-bootstrap': 19.0.1(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@popperjs/core@2.11.8)(rxjs@7.8.2)
ngx-ui-tour-core: 15.0.0(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2)
'@ng-bootstrap/ng-bootstrap': 19.0.1(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.2(@angular/compiler-cli@20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3))(@angular/compiler@20.3.2))(@popperjs/core@2.11.8)(rxjs@7.8.2)
ngx-ui-tour-core: 15.0.0(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/animations@20.3.12(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2)
tslib: 2.8.1
transitivePeerDependencies:
- '@angular/router'

View file

@ -0,0 +1,126 @@
@if (hasSuggestions) {
<div class="ai-suggestions-panel card shadow-sm mb-3" [@slideIn]>
<div class="card-header bg-primary text-white d-flex align-items-center justify-content-between" role="button" (click)="toggleCollapse()">
<div class="d-flex align-items-center">
<i-bs name="magic" width="1.2em" height="1.2em" class="me-2"></i-bs>
<strong i18n>AI Suggestions</strong>
<span class="badge bg-light text-primary ms-2">{{ pendingSuggestions.length }}</span>
</div>
<div class="d-flex align-items-center gap-2">
@if (appliedCount > 0) {
<span class="badge bg-success" i18n>{{ appliedCount }} applied</span>
}
@if (rejectedCount > 0) {
<span class="badge bg-danger" i18n>{{ rejectedCount }} rejected</span>
}
<i-bs [name]="isCollapsed ? 'chevron-down' : 'chevron-up'" width="1em" height="1em"></i-bs>
</div>
</div>
<div [ngbCollapse]="isCollapsed" class="card-body">
<div class="mb-3 pb-2 border-bottom">
<p class="text-muted small mb-2" i18n>
<i-bs name="info-circle" width="0.9em" height="0.9em" class="me-1"></i-bs>
AI has analyzed this document and suggests the following metadata. Review and apply or reject each suggestion.
</p>
<div class="d-flex gap-2 flex-wrap">
<button
type="button"
class="btn btn-sm btn-success"
[disabled]="disabled || pendingSuggestions.length === 0"
(click)="applyAll()"
i18n>
<i-bs name="check-all" width="1em" height="1em" class="me-1"></i-bs>
Apply All
</button>
<button
type="button"
class="btn btn-sm btn-outline-danger"
[disabled]="disabled || pendingSuggestions.length === 0"
(click)="rejectAll()"
i18n>
<i-bs name="x-circle" width="1em" height="1em" class="me-1"></i-bs>
Reject All
</button>
</div>
</div>
<div class="suggestions-container">
@for (type of suggestionTypes; track type) {
<div class="suggestion-group mb-3">
<div class="suggestion-group-header d-flex align-items-center mb-2">
<i-bs [name]="getTypeIcon(type)" width="1.1em" height="1.1em" class="me-2 text-primary"></i-bs>
<strong class="text-secondary">{{ getTypeLabel(type) }}</strong>
<span class="badge bg-secondary ms-2">{{ groupedSuggestions.get(type)?.length }}</span>
</div>
<div class="suggestion-items">
@for (suggestion of groupedSuggestions.get(type); track suggestion.id) {
<div
class="suggestion-item card mb-2"
[@fadeInOut]
[class.suggestion-applying]="suggestion.status === AISuggestionStatus.Applied"
[class.suggestion-rejecting]="suggestion.status === AISuggestionStatus.Rejected">
<div class="card-body p-2">
<div class="d-flex align-items-start justify-content-between gap-2">
<div class="flex-grow-1">
<div class="suggestion-value fw-medium mb-1">
@if (suggestion.type === AISuggestionType.CustomField && suggestion.field_name) {
<span class="text-muted small me-1">{{ suggestion.field_name }}:</span>
}
{{ getLabel(suggestion) }}
</div>
<div class="d-flex align-items-center gap-2 flex-wrap">
<span
class="confidence-badge badge"
[ngClass]="getConfidenceClass(suggestion.confidence)">
<i-bs [name]="getConfidenceIcon(suggestion.confidence)" width="0.8em" height="0.8em" class="me-1"></i-bs>
{{ getConfidenceLabel(suggestion.confidence) }}
</span>
@if (suggestion.created_at) {
<span class="text-muted small">
<i-bs name="clock" width="0.8em" height="0.8em" class="me-1"></i-bs>
{{ suggestion.created_at | date:'short' }}
</span>
}
</div>
</div>
<div class="suggestion-actions d-flex gap-1 flex-shrink-0">
<button
type="button"
class="btn btn-sm btn-success"
[disabled]="disabled"
(click)="applySuggestion(suggestion)"
i18n-title
title="Apply this suggestion">
<i-bs name="check-lg" width="1em" height="1em"></i-bs>
</button>
<button
type="button"
class="btn btn-sm btn-outline-danger"
[disabled]="disabled"
(click)="rejectSuggestion(suggestion)"
i18n-title
title="Reject this suggestion">
<i-bs name="x-lg" width="1em" height="1em"></i-bs>
</button>
</div>
</div>
</div>
</div>
}
</div>
</div>
}
</div>
@if (pendingSuggestions.length === 0) {
<div class="text-center text-muted py-3">
<i-bs name="check-circle" width="2em" height="2em" class="mb-2"></i-bs>
<p i18n>All suggestions have been processed</p>
</div>
}
</div>
</div>
}

View file

@ -0,0 +1,241 @@
.ai-suggestions-panel {
border: 2px solid var(--bs-primary);
border-radius: 0.5rem;
overflow: hidden;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
.card-header {
cursor: pointer;
user-select: none;
transition: background-color 0.2s ease;
padding: 0.75rem 1rem;
&:hover {
background-color: var(--bs-primary) !important;
filter: brightness(1.1);
}
.badge {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
}
.card-body {
padding: 1rem;
}
}
.suggestions-container {
max-height: 600px;
overflow-y: auto;
overflow-x: hidden;
// Custom scrollbar styles
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
&:hover {
background: #555;
}
}
}
.suggestion-group {
.suggestion-group-header {
padding-bottom: 0.5rem;
border-bottom: 1px solid #dee2e6;
strong {
font-size: 0.95rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge {
font-size: 0.7rem;
}
}
.suggestion-items {
padding-left: 1.5rem;
}
}
.suggestion-item {
border-left: 3px solid var(--bs-primary);
transition: all 0.3s ease;
position: relative;
&:hover {
border-left-color: var(--bs-success);
box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.1);
transform: translateX(2px);
}
&.suggestion-applying {
animation: applyAnimation 0.5s ease;
border-left-color: var(--bs-success);
background-color: rgba(25, 135, 84, 0.1);
}
&.suggestion-rejecting {
animation: rejectAnimation 0.5s ease;
border-left-color: var(--bs-danger);
background-color: rgba(220, 53, 69, 0.1);
}
.suggestion-value {
color: #333;
font-size: 0.95rem;
word-break: break-word;
}
.confidence-badge {
font-size: 0.75rem;
font-weight: 500;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
display: inline-flex;
align-items: center;
&.confidence-high {
background-color: #28a745;
color: white;
}
&.confidence-medium {
background-color: #ffc107;
color: #333;
}
&.confidence-low {
background-color: #dc3545;
color: white;
}
}
.suggestion-actions {
.btn {
min-width: 36px;
padding: 0.25rem 0.5rem;
transition: all 0.2s ease;
&:hover {
transform: scale(1.1);
}
&:active {
transform: scale(0.95);
}
}
}
}
// Animations
@keyframes applyAnimation {
0% {
opacity: 1;
transform: translateX(0);
}
50% {
opacity: 0.5;
transform: translateX(20px);
}
100% {
opacity: 0;
transform: translateX(40px);
}
}
@keyframes rejectAnimation {
0% {
opacity: 1;
transform: translateX(0) rotate(0deg);
}
50% {
opacity: 0.5;
transform: translateX(-20px) rotate(-5deg);
}
100% {
opacity: 0;
transform: translateX(-40px) rotate(-10deg);
}
}
// Responsive design
@media (max-width: 768px) {
.ai-suggestions-panel {
.card-header {
padding: 0.5rem 0.75rem;
flex-wrap: wrap;
.badge {
font-size: 0.65rem;
padding: 0.2rem 0.4rem;
}
}
.card-body {
padding: 0.75rem;
}
}
.suggestions-container {
max-height: 400px;
}
.suggestion-group {
.suggestion-items {
padding-left: 0.5rem;
}
}
.suggestion-item {
.d-flex {
flex-direction: column;
gap: 0.5rem !important;
}
.suggestion-actions {
width: 100%;
justify-content: flex-end;
}
}
}
@media (max-width: 576px) {
.ai-suggestions-panel {
.card-header {
.d-flex {
flex-direction: column;
align-items: flex-start !important;
gap: 0.5rem;
}
}
}
.suggestion-item {
.suggestion-value {
font-size: 0.875rem;
}
.confidence-badge {
font-size: 0.7rem;
}
}
}

View file

@ -0,0 +1,331 @@
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { provideAnimations } from '@angular/platform-browser/animations'
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { of } from 'rxjs'
import {
AISuggestion,
AISuggestionStatus,
AISuggestionType,
} from 'src/app/data/ai-suggestion'
import { Correspondent } from 'src/app/data/correspondent'
import { DocumentType } from 'src/app/data/document-type'
import { StoragePath } from 'src/app/data/storage-path'
import { Tag } from 'src/app/data/tag'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
import { AiSuggestionsPanelComponent } from './ai-suggestions-panel.component'
const mockTags: Tag[] = [
{ id: 1, name: 'Invoice', colour: '#ff0000', text_colour: '#ffffff' },
{ id: 2, name: 'Receipt', colour: '#00ff00', text_colour: '#000000' },
]
const mockCorrespondents: Correspondent[] = [
{ id: 1, name: 'Acme Corp' },
{ id: 2, name: 'TechStart LLC' },
]
const mockDocumentTypes: DocumentType[] = [
{ id: 1, name: 'Invoice' },
{ id: 2, name: 'Contract' },
]
const mockStoragePaths: StoragePath[] = [
{ id: 1, name: '/invoices', path: '/invoices' },
{ id: 2, name: '/contracts', path: '/contracts' },
]
const mockSuggestions: AISuggestion[] = [
{
id: '1',
type: AISuggestionType.Tag,
value: 1,
confidence: 0.85,
status: AISuggestionStatus.Pending,
},
{
id: '2',
type: AISuggestionType.Correspondent,
value: 1,
confidence: 0.75,
status: AISuggestionStatus.Pending,
},
{
id: '3',
type: AISuggestionType.DocumentType,
value: 1,
confidence: 0.90,
status: AISuggestionStatus.Pending,
},
]
describe('AiSuggestionsPanelComponent', () => {
let component: AiSuggestionsPanelComponent
let fixture: ComponentFixture<AiSuggestionsPanelComponent>
let tagService: TagService
let correspondentService: CorrespondentService
let documentTypeService: DocumentTypeService
let storagePathService: StoragePathService
let customFieldsService: CustomFieldsService
let toastService: ToastService
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
AiSuggestionsPanelComponent,
NgbCollapseModule,
NgxBootstrapIconsModule.pick(allIcons),
],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
provideAnimations(),
],
}).compileComponents()
tagService = TestBed.inject(TagService)
correspondentService = TestBed.inject(CorrespondentService)
documentTypeService = TestBed.inject(DocumentTypeService)
storagePathService = TestBed.inject(StoragePathService)
customFieldsService = TestBed.inject(CustomFieldsService)
toastService = TestBed.inject(ToastService)
jest.spyOn(tagService, 'listAll').mockReturnValue(
of({
all: mockTags.map((t) => t.id),
count: mockTags.length,
results: mockTags,
})
)
jest.spyOn(correspondentService, 'listAll').mockReturnValue(
of({
all: mockCorrespondents.map((c) => c.id),
count: mockCorrespondents.length,
results: mockCorrespondents,
})
)
jest.spyOn(documentTypeService, 'listAll').mockReturnValue(
of({
all: mockDocumentTypes.map((dt) => dt.id),
count: mockDocumentTypes.length,
results: mockDocumentTypes,
})
)
jest.spyOn(storagePathService, 'listAll').mockReturnValue(
of({
all: mockStoragePaths.map((sp) => sp.id),
count: mockStoragePaths.length,
results: mockStoragePaths,
})
)
jest.spyOn(customFieldsService, 'listAll').mockReturnValue(
of({
all: [],
count: 0,
results: [],
})
)
fixture = TestBed.createComponent(AiSuggestionsPanelComponent)
component = fixture.componentInstance
})
it('should create', () => {
expect(component).toBeTruthy()
})
it('should process suggestions on input change', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
expect(component.pendingSuggestions.length).toBe(3)
expect(component.appliedCount).toBe(0)
expect(component.rejectedCount).toBe(0)
})
it('should group suggestions by type', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
expect(component.groupedSuggestions.size).toBe(3)
expect(component.groupedSuggestions.get(AISuggestionType.Tag)?.length).toBe(
1
)
expect(
component.groupedSuggestions.get(AISuggestionType.Correspondent)?.length
).toBe(1)
expect(
component.groupedSuggestions.get(AISuggestionType.DocumentType)?.length
).toBe(1)
})
it('should apply a suggestion', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
const toastSpy = jest.spyOn(toastService, 'showInfo')
const applySpy = jest.spyOn(component.apply, 'emit')
const suggestion = component.pendingSuggestions[0]
component.applySuggestion(suggestion)
expect(suggestion.status).toBe(AISuggestionStatus.Applied)
expect(applySpy).toHaveBeenCalledWith(suggestion)
expect(toastSpy).toHaveBeenCalled()
})
it('should reject a suggestion', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
const toastSpy = jest.spyOn(toastService, 'showInfo')
const rejectSpy = jest.spyOn(component.reject, 'emit')
const suggestion = component.pendingSuggestions[0]
component.rejectSuggestion(suggestion)
expect(suggestion.status).toBe(AISuggestionStatus.Rejected)
expect(rejectSpy).toHaveBeenCalledWith(suggestion)
expect(toastSpy).toHaveBeenCalled()
})
it('should apply all suggestions', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
const toastSpy = jest.spyOn(toastService, 'showInfo')
const applySpy = jest.spyOn(component.apply, 'emit')
component.applyAll()
expect(applySpy).toHaveBeenCalledTimes(3)
expect(toastSpy).toHaveBeenCalled()
})
it('should reject all suggestions', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
const toastSpy = jest.spyOn(toastService, 'showInfo')
const rejectSpy = jest.spyOn(component.reject, 'emit')
component.rejectAll()
expect(rejectSpy).toHaveBeenCalledTimes(3)
expect(toastSpy).toHaveBeenCalled()
})
it('should return correct confidence class', () => {
expect(component.getConfidenceClass(0.9)).toBe('confidence-high')
expect(component.getConfidenceClass(0.7)).toBe('confidence-medium')
expect(component.getConfidenceClass(0.5)).toBe('confidence-low')
})
it('should return correct confidence label', () => {
expect(component.getConfidenceLabel(0.85)).toContain('85%')
expect(component.getConfidenceLabel(0.65)).toContain('65%')
expect(component.getConfidenceLabel(0.45)).toContain('45%')
})
it('should toggle collapse', () => {
expect(component.isCollapsed).toBe(false)
component.toggleCollapse()
expect(component.isCollapsed).toBe(true)
component.toggleCollapse()
expect(component.isCollapsed).toBe(false)
})
it('should respect disabled state', () => {
component.suggestions = mockSuggestions
component.disabled = true
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
const applySpy = jest.spyOn(component.apply, 'emit')
const suggestion = component.pendingSuggestions[0]
component.applySuggestion(suggestion)
expect(applySpy).not.toHaveBeenCalled()
})
it('should not render panel when there are no suggestions', () => {
component.suggestions = []
fixture.detectChanges()
expect(component.hasSuggestions).toBe(false)
})
it('should render panel when there are suggestions', () => {
component.suggestions = mockSuggestions
component.ngOnChanges({
suggestions: {
currentValue: mockSuggestions,
previousValue: [],
firstChange: true,
isFirstChange: () => true,
},
})
fixture.detectChanges()
expect(component.hasSuggestions).toBe(true)
})
})

View file

@ -0,0 +1,381 @@
import { CommonModule } from '@angular/common'
import {
trigger,
state,
style,
transition,
animate,
} from '@angular/animations'
import {
Component,
EventEmitter,
Input,
OnChanges,
Output,
SimpleChanges,
inject,
} from '@angular/core'
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import {
AISuggestion,
AISuggestionStatus,
AISuggestionType,
} from 'src/app/data/ai-suggestion'
import { Correspondent } from 'src/app/data/correspondent'
import { CustomField } from 'src/app/data/custom-field'
import { DocumentType } from 'src/app/data/document-type'
import { StoragePath } from 'src/app/data/storage-path'
import { Tag } from 'src/app/data/tag'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'pngx-ai-suggestions-panel',
templateUrl: './ai-suggestions-panel.component.html',
styleUrls: ['./ai-suggestions-panel.component.scss'],
imports: [
CommonModule,
NgbCollapseModule,
NgxBootstrapIconsModule,
],
animations: [
trigger('slideIn', [
transition(':enter', [
style({ transform: 'translateY(-20px)', opacity: 0 }),
animate('300ms ease-out', style({ transform: 'translateY(0)', opacity: 1 })),
]),
]),
trigger('fadeInOut', [
transition(':enter', [
style({ opacity: 0, transform: 'scale(0.95)' }),
animate('200ms ease-out', style({ opacity: 1, transform: 'scale(1)' })),
]),
transition(':leave', [
animate('200ms ease-in', style({ opacity: 0, transform: 'scale(0.95)' })),
]),
]),
],
})
export class AiSuggestionsPanelComponent implements OnChanges {
private tagService = inject(TagService)
private correspondentService = inject(CorrespondentService)
private documentTypeService = inject(DocumentTypeService)
private storagePathService = inject(StoragePathService)
private customFieldsService = inject(CustomFieldsService)
private toastService = inject(ToastService)
@Input()
suggestions: AISuggestion[] = []
@Input()
disabled: boolean = false
@Output()
apply = new EventEmitter<AISuggestion>()
@Output()
reject = new EventEmitter<AISuggestion>()
public isCollapsed = false
public pendingSuggestions: AISuggestion[] = []
public groupedSuggestions: Map<AISuggestionType, AISuggestion[]> = new Map()
public appliedCount = 0
public rejectedCount = 0
private tags: Tag[] = []
private correspondents: Correspondent[] = []
private documentTypes: DocumentType[] = []
private storagePaths: StoragePath[] = []
private customFields: CustomField[] = []
public AISuggestionType = AISuggestionType
public AISuggestionStatus = AISuggestionStatus
ngOnChanges(changes: SimpleChanges): void {
if (changes['suggestions']) {
this.processSuggestions()
this.loadMetadata()
}
}
private processSuggestions(): void {
this.pendingSuggestions = this.suggestions.filter(
(s) => s.status === AISuggestionStatus.Pending
)
this.appliedCount = this.suggestions.filter(
(s) => s.status === AISuggestionStatus.Applied
).length
this.rejectedCount = this.suggestions.filter(
(s) => s.status === AISuggestionStatus.Rejected
).length
// Group suggestions by type
this.groupedSuggestions.clear()
this.pendingSuggestions.forEach((suggestion) => {
const group = this.groupedSuggestions.get(suggestion.type) || []
group.push(suggestion)
this.groupedSuggestions.set(suggestion.type, group)
})
}
private loadMetadata(): void {
// Load tags if needed
const tagSuggestions = this.pendingSuggestions.filter(
(s) => s.type === AISuggestionType.Tag
)
if (tagSuggestions.length > 0) {
this.tagService.listAll().subscribe((tags) => {
this.tags = tags.results
this.updateSuggestionLabels()
})
}
// Load correspondents if needed
const correspondentSuggestions = this.pendingSuggestions.filter(
(s) => s.type === AISuggestionType.Correspondent
)
if (correspondentSuggestions.length > 0) {
this.correspondentService.listAll().subscribe((correspondents) => {
this.correspondents = correspondents.results
this.updateSuggestionLabels()
})
}
// Load document types if needed
const documentTypeSuggestions = this.pendingSuggestions.filter(
(s) => s.type === AISuggestionType.DocumentType
)
if (documentTypeSuggestions.length > 0) {
this.documentTypeService.listAll().subscribe((documentTypes) => {
this.documentTypes = documentTypes.results
this.updateSuggestionLabels()
})
}
// Load storage paths if needed
const storagePathSuggestions = this.pendingSuggestions.filter(
(s) => s.type === AISuggestionType.StoragePath
)
if (storagePathSuggestions.length > 0) {
this.storagePathService.listAll().subscribe((storagePaths) => {
this.storagePaths = storagePaths.results
this.updateSuggestionLabels()
})
}
// Load custom fields if needed
const customFieldSuggestions = this.pendingSuggestions.filter(
(s) => s.type === AISuggestionType.CustomField
)
if (customFieldSuggestions.length > 0) {
this.customFieldsService.listAll().subscribe((customFields) => {
this.customFields = customFields.results
this.updateSuggestionLabels()
})
}
}
private updateSuggestionLabels(): void {
this.pendingSuggestions.forEach((suggestion) => {
if (!suggestion.label) {
suggestion.label = this.getLabel(suggestion)
}
})
}
public getLabel(suggestion: AISuggestion): string {
if (suggestion.label) {
return suggestion.label
}
switch (suggestion.type) {
case AISuggestionType.Tag:
const tag = this.tags.find((t) => t.id === suggestion.value)
return tag ? tag.name : `Tag #${suggestion.value}`
case AISuggestionType.Correspondent:
const correspondent = this.correspondents.find(
(c) => c.id === suggestion.value
)
return correspondent
? correspondent.name
: `Correspondent #${suggestion.value}`
case AISuggestionType.DocumentType:
const docType = this.documentTypes.find(
(dt) => dt.id === suggestion.value
)
return docType ? docType.name : `Document Type #${suggestion.value}`
case AISuggestionType.StoragePath:
const storagePath = this.storagePaths.find(
(sp) => sp.id === suggestion.value
)
return storagePath ? storagePath.name : `Storage Path #${suggestion.value}`
case AISuggestionType.CustomField:
return suggestion.field_name || 'Custom Field'
case AISuggestionType.Date:
return new Date(suggestion.value).toLocaleDateString()
case AISuggestionType.Title:
return suggestion.value
default:
return String(suggestion.value)
}
}
public getTypeLabel(type: AISuggestionType): string {
switch (type) {
case AISuggestionType.Tag:
return $localize`Tags`
case AISuggestionType.Correspondent:
return $localize`Correspondent`
case AISuggestionType.DocumentType:
return $localize`Document Type`
case AISuggestionType.StoragePath:
return $localize`Storage Path`
case AISuggestionType.CustomField:
return $localize`Custom Field`
case AISuggestionType.Date:
return $localize`Date`
case AISuggestionType.Title:
return $localize`Title`
default:
return String(type)
}
}
public getTypeIcon(type: AISuggestionType): string {
switch (type) {
case AISuggestionType.Tag:
return 'tag'
case AISuggestionType.Correspondent:
return 'person'
case AISuggestionType.DocumentType:
return 'file-earmark-text'
case AISuggestionType.StoragePath:
return 'folder'
case AISuggestionType.CustomField:
return 'input-cursor-text'
case AISuggestionType.Date:
return 'calendar'
case AISuggestionType.Title:
return 'pencil'
default:
return 'lightbulb'
}
}
public getConfidenceClass(confidence: number): string {
if (confidence >= 0.8) {
return 'confidence-high'
} else if (confidence >= 0.6) {
return 'confidence-medium'
} else {
return 'confidence-low'
}
}
public getConfidenceLabel(confidence: number): string {
const percentage = Math.round(confidence * 100)
if (confidence >= 0.8) {
return $localize`High (${percentage}%)`
} else if (confidence >= 0.6) {
return $localize`Medium (${percentage}%)`
} else {
return $localize`Low (${percentage}%)`
}
}
public getConfidenceIcon(confidence: number): string {
if (confidence >= 0.8) {
return 'check-circle-fill'
} else if (confidence >= 0.6) {
return 'exclamation-circle'
} else {
return 'question-circle'
}
}
public applySuggestion(suggestion: AISuggestion): void {
if (this.disabled) {
return
}
suggestion.status = AISuggestionStatus.Applied
this.apply.emit(suggestion)
this.processSuggestions()
this.toastService.showInfo(
$localize`Applied AI suggestion: ${this.getLabel(suggestion)}`
)
}
public rejectSuggestion(suggestion: AISuggestion): void {
if (this.disabled) {
return
}
suggestion.status = AISuggestionStatus.Rejected
this.reject.emit(suggestion)
this.processSuggestions()
this.toastService.showInfo(
$localize`Rejected AI suggestion: ${this.getLabel(suggestion)}`
)
}
public applyAll(): void {
if (this.disabled) {
return
}
const count = this.pendingSuggestions.length
this.pendingSuggestions.forEach((suggestion) => {
suggestion.status = AISuggestionStatus.Applied
this.apply.emit(suggestion)
})
this.processSuggestions()
this.toastService.showInfo(
$localize`Applied ${count} AI suggestions`
)
}
public rejectAll(): void {
if (this.disabled) {
return
}
const count = this.pendingSuggestions.length
this.pendingSuggestions.forEach((suggestion) => {
suggestion.status = AISuggestionStatus.Rejected
this.reject.emit(suggestion)
})
this.processSuggestions()
this.toastService.showInfo(
$localize`Rejected ${count} AI suggestions`
)
}
public toggleCollapse(): void {
this.isCollapsed = !this.isCollapsed
}
public get hasSuggestions(): boolean {
return this.pendingSuggestions.length > 0
}
public get suggestionTypes(): AISuggestionType[] {
return Array.from(this.groupedSuggestions.keys())
}
}

View file

@ -118,6 +118,13 @@
<ng-container *ngTemplateOutlet="saveButtons"></ng-container>
</div>
<pngx-ai-suggestions-panel
[suggestions]="aiSuggestions"
[disabled]="!userCanEdit"
(apply)="onApplySuggestion($event)"
(reject)="onRejectSuggestion($event)">
</pngx-ai-suggestions-panel>
<ul ngbNav #nav="ngbNav" class="nav-underline flex-nowrap flex-md-wrap overflow-auto" (navChange)="onNavChange($event)" [(activeId)]="activeNavID">
<li [ngbNavItem]="DocumentDetailNavIDs.Details">
<a ngbNavLink i18n>Details</a>

View file

@ -32,6 +32,11 @@ import {
switchMap,
takeUntil,
} from 'rxjs/operators'
import {
AISuggestion,
AISuggestionStatus,
AISuggestionType,
} from 'src/app/data/ai-suggestion'
import { Correspondent } from 'src/app/data/correspondent'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import { CustomFieldInstance } from 'src/app/data/custom-field-instance'
@ -109,6 +114,7 @@ import { ShareLinksDialogComponent } from '../common/share-links-dialog/share-li
import { DocumentHistoryComponent } from '../document-history/document-history.component'
import { DocumentNotesComponent } from '../document-notes/document-notes.component'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import { AiSuggestionsPanelComponent } from '../ai-suggestions-panel/ai-suggestions-panel.component'
import { MetadataCollapseComponent } from './metadata-collapse/metadata-collapse.component'
enum DocumentDetailNavIDs {
@ -151,6 +157,7 @@ export enum ZoomSetting {
CustomFieldsDropdownComponent,
DocumentNotesComponent,
DocumentHistoryComponent,
AiSuggestionsPanelComponent,
CheckComponent,
DateComponent,
DocumentLinkComponent,
@ -216,6 +223,7 @@ export class DocumentDetailComponent
document: Document
metadata: DocumentMetadata
suggestions: DocumentSuggestions
aiSuggestions: AISuggestion[] = []
users: User[]
title: string
@ -437,6 +445,7 @@ export class DocumentDetailComponent
}
this.documentId = doc.id
this.suggestions = null
this.aiSuggestions = []
const openDocument = this.openDocumentService.getOpenDocument(
this.documentId
)
@ -691,9 +700,11 @@ export class DocumentDetailComponent
.subscribe({
next: (result) => {
this.suggestions = result
this.aiSuggestions = this.convertSuggestionsToAI(result)
},
error: (error) => {
this.suggestions = null
this.aiSuggestions = []
this.toastService.showError(
$localize`Error retrieving suggestions.`,
error
@ -1542,4 +1553,124 @@ export class DocumentDetailComponent
},
})
}
private convertSuggestionsToAI(suggestions: DocumentSuggestions): AISuggestion[] {
if (!suggestions) {
return []
}
const aiSuggestions: AISuggestion[] = []
let id = 1
// Convert tag suggestions
if (suggestions.tags && suggestions.tags.length > 0) {
suggestions.tags.forEach((tagId) => {
aiSuggestions.push({
id: `tag-${id++}`,
type: AISuggestionType.Tag,
value: tagId,
confidence: 0.75, // Default confidence for legacy suggestions
status: AISuggestionStatus.Pending,
})
})
}
// Convert correspondent suggestions
if (suggestions.correspondents && suggestions.correspondents.length > 0) {
suggestions.correspondents.forEach((corrId) => {
aiSuggestions.push({
id: `correspondent-${id++}`,
type: AISuggestionType.Correspondent,
value: corrId,
confidence: 0.75,
status: AISuggestionStatus.Pending,
})
})
}
// Convert document type suggestions
if (suggestions.document_types && suggestions.document_types.length > 0) {
suggestions.document_types.forEach((docTypeId) => {
aiSuggestions.push({
id: `doctype-${id++}`,
type: AISuggestionType.DocumentType,
value: docTypeId,
confidence: 0.75,
status: AISuggestionStatus.Pending,
})
})
}
// Convert storage path suggestions
if (suggestions.storage_paths && suggestions.storage_paths.length > 0) {
suggestions.storage_paths.forEach((storagePathId) => {
aiSuggestions.push({
id: `storage-${id++}`,
type: AISuggestionType.StoragePath,
value: storagePathId,
confidence: 0.75,
status: AISuggestionStatus.Pending,
})
})
}
// Convert date suggestions
if (suggestions.dates && suggestions.dates.length > 0) {
suggestions.dates.forEach((date) => {
aiSuggestions.push({
id: `date-${id++}`,
type: AISuggestionType.Date,
value: date,
confidence: 0.75,
status: AISuggestionStatus.Pending,
})
})
}
return aiSuggestions
}
onApplySuggestion(suggestion: AISuggestion): void {
switch (suggestion.type) {
case AISuggestionType.Tag:
const currentTags = this.documentForm.get('tags').value || []
if (!currentTags.includes(suggestion.value)) {
this.documentForm.get('tags').setValue([...currentTags, suggestion.value])
this.documentForm.get('tags').markAsDirty()
}
break
case AISuggestionType.Correspondent:
this.documentForm.get('correspondent').setValue(suggestion.value)
this.documentForm.get('correspondent').markAsDirty()
break
case AISuggestionType.DocumentType:
this.documentForm.get('document_type').setValue(suggestion.value)
this.documentForm.get('document_type').markAsDirty()
break
case AISuggestionType.StoragePath:
this.documentForm.get('storage_path').setValue(suggestion.value)
this.documentForm.get('storage_path').markAsDirty()
break
case AISuggestionType.Date:
const dateAdapter = new ISODateAdapter()
const dateValue = dateAdapter.fromModel(suggestion.value)
this.documentForm.get('created').setValue(dateValue)
this.documentForm.get('created').markAsDirty()
break
case AISuggestionType.Title:
this.documentForm.get('title').setValue(suggestion.value)
this.documentForm.get('title').markAsDirty()
break
}
}
onRejectSuggestion(suggestion: AISuggestion): void {
// Just remove it from the list (handled by the panel component)
// No additional action needed here
}
}

View file

@ -0,0 +1,32 @@
export enum AISuggestionType {
Tag = 'tag',
Correspondent = 'correspondent',
DocumentType = 'document_type',
StoragePath = 'storage_path',
CustomField = 'custom_field',
Date = 'date',
Title = 'title',
}
export enum AISuggestionStatus {
Pending = 'pending',
Applied = 'applied',
Rejected = 'rejected',
}
export interface AISuggestion {
id: string
type: AISuggestionType
value: any
confidence: number
status: AISuggestionStatus
label?: string
field_name?: string // For custom fields
created_at?: Date
}
export interface AIDocumentSuggestions {
document_id: number
suggestions: AISuggestion[]
generated_at: Date
}

View file

@ -13,6 +13,7 @@ import {
} from '@angular/common/http'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'
import { provideAnimations } from '@angular/platform-browser/animations'
import {
NgbDateAdapter,
NgbDateParserFormatter,
@ -56,11 +57,14 @@ import {
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
chevronDown,
chevronRight,
chevronUp,
clipboard,
clipboardCheck,
clipboardCheckFill,
clipboardFill,
clock,
clockHistory,
dash,
dashCircle,
@ -71,6 +75,7 @@ import {
envelope,
envelopeAt,
envelopeAtFill,
exclamationCircle,
exclamationCircleFill,
exclamationTriangle,
exclamationTriangleFill,
@ -81,6 +86,7 @@ import {
fileEarmarkLock,
fileEarmarkMinus,
fileEarmarkRichtext,
fileEarmarkText,
fileText,
files,
filter,
@ -95,11 +101,14 @@ import {
hddStack,
house,
infoCircle,
inputCursorText,
journals,
lightbulb,
link,
listNested,
listTask,
listUl,
magic,
microsoft,
nodePlus,
pencil,
@ -270,11 +279,14 @@ const icons = {
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
chevronDown,
chevronRight,
chevronUp,
clipboard,
clipboardCheck,
clipboardCheckFill,
clipboardFill,
clock,
clockHistory,
dash,
dashCircle,
@ -285,6 +297,7 @@ const icons = {
envelope,
envelopeAt,
envelopeAtFill,
exclamationCircle,
exclamationCircleFill,
exclamationTriangle,
exclamationTriangleFill,
@ -295,6 +308,7 @@ const icons = {
fileEarmarkLock,
fileEarmarkMinus,
fileEarmarkRichtext,
fileEarmarkText,
files,
fileText,
filter,
@ -309,11 +323,14 @@ const icons = {
hddStack,
house,
infoCircle,
inputCursorText,
journals,
lightbulb,
link,
listNested,
listTask,
listUl,
magic,
microsoft,
nodePlus,
pencil,
@ -402,5 +419,6 @@ bootstrapApplication(AppComponent, {
DocumentTypeNamePipe,
StoragePathNamePipe,
provideHttpClient(withInterceptorsFromDi()),
provideAnimations(),
],
}).catch((err) => console.error(err))