From b254ed538573f8a30d42c418c80034adedfb7882 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Thu, 12 Sep 2024 13:09:50 +0200 Subject: [PATCH 01/11] ci: update download source for chrome images --- build/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 932e8867e2..1e38b62981 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -35,9 +35,8 @@ COPY --from=base-with-dependencies /opt/app /opt/app RUN apt-get update && apt-get install -y curl WORKDIR /tmp -# 120.0.6099.224-1~deb11u1 -> 2024-02-01 -# 128.0.6613.113-1~deb12u1-> 2024-08-30 -ARG CHROMIUM_VERSION=128.0.6613.113-1~deb12u1 +# 128.0.6613.84-1~deb12u1 -> 2024-08-23 +ARG CHROMIUM_VERSION=128.0.6613.84-1~deb12u1 RUN curl -o chromium-common.deb https://ftp.debian.org/debian/pool/main/c/chromium/chromium-common_${CHROMIUM_VERSION}_$(dpkg --print-architecture).deb RUN curl -o chromium.deb https://ftp.debian.org/debian/pool/main/c/chromium/chromium_${CHROMIUM_VERSION}_$(dpkg --print-architecture).deb RUN apt-get install -y ./chromium-common.deb ./chromium.deb build-essential git libssl-dev From ae9e1036c9f0cc152d4776b4493b6cc7a5aa6e90 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Thu, 12 Sep 2024 11:52:36 +0200 Subject: [PATCH 02/11] test: fix storybook stories --- .../common-components/entity-select/entity-select.stories.ts | 4 +--- .../entity-count-dashboard/entity-count-dashboard.stories.ts | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/core/common-components/entity-select/entity-select.stories.ts b/src/app/core/common-components/entity-select/entity-select.stories.ts index f9375fd530..8777e9237a 100644 --- a/src/app/core/common-components/entity-select/entity-select.stories.ts +++ b/src/app/core/common-components/entity-select/entity-select.stories.ts @@ -1,8 +1,6 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { EntitySelectComponent } from "./entity-select.component"; import { StorybookBaseModule } from "../../../utils/storybook-base.module"; -import { componentRegistry } from "../../../dynamic-components"; -import { ChildBlockComponent } from "../../../child-dev-project/children/child-block/child-block.component"; import { importProvidersFrom } from "@angular/core"; import { FormControl } from "@angular/forms"; import { TestEntity } from "../../../utils/test-utils/TestEntity"; @@ -51,7 +49,7 @@ export default { }, } as Meta; -componentRegistry.add("ChildBlock", async () => ChildBlockComponent); +//componentRegistry.add("EntityBlock", async () => EntityBlockComponent); const Template: StoryFn> = ( args: EntitySelectComponent, diff --git a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts index b0c5adcfb9..2d5e954392 100644 --- a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts +++ b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts @@ -33,4 +33,7 @@ const Template: StoryFn = ( }); export const Primary = Template.bind({}); -Primary.args = {}; +Primary.args = { + entityType: "TestEntity", + groupBy: "category", +}; From a1dcea8edd21bdbf14b976221ffb2a425c01b4c8 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Thu, 12 Sep 2024 12:04:25 +0200 Subject: [PATCH 03/11] fix(demo): correctly generate category values again demo generated center, gender, etc. have been broken --- .../aser/demo-aser-generator.service.ts | 6 +++--- .../demo-data-generators/demo-child-generator.service.ts | 4 ++-- .../demo-educational-material-generator.service.ts | 2 +- .../observations/demo-historical-data-generator.ts | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts index c627814040..26bc8c6ed3 100644 --- a/src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts @@ -51,15 +51,15 @@ export class DemoAserGeneratorService extends DemoDataGenerator { aserResult.math = this.selectNextSkillLevel( mathLevels.slice(1), previousResult.math, - ).id; + ); aserResult.english = this.selectNextSkillLevel( readingLevels.slice(1), previousResult.english, - ).id; + ); aserResult[firstLanguage] = this.selectNextSkillLevel( readingLevels.slice(1), previousResult[firstLanguage], - ).id; + ); data.push(aserResult); diff --git a/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts index f219e586d6..c4d27547f6 100644 --- a/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts @@ -36,10 +36,10 @@ export class DemoChildGenerator extends DemoDataGenerator { child.name = faker.person.firstName() + " " + faker.person.lastName(); child.projectNumber = id; child.religion = faker.helpers.arrayElement(religions); - child.gender = faker.helpers.arrayElement(genders.slice(1)).id; + child.gender = faker.helpers.arrayElement(genders.slice(1)); child.dateOfBirth = new DateWithAge(faker.dateOfBirth(5, 20)); child.motherTongue = faker.helpers.arrayElement(languages); - child.center = faker.helpers.arrayElement(centersWithProbability).id; + child.center = faker.helpers.arrayElement(centersWithProbability); child.phone = "+" + faker.number.int({ min: 10, max: 99 }) + diff --git a/src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts index 1c7edb6fd9..7eb1ea5107 100644 --- a/src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts @@ -72,7 +72,7 @@ export class DemoEducationalMaterialGeneratorService extends DemoDataGenerator { const historicalDataOfChild = [...Array(countOfData)].map(() => { const historicalData = createEntityOfType("HistoricalEntityData"); for (const attribute of attributes) { - historicalData[attribute] = - faker.helpers.arrayElement(ratingAnswers).id; + historicalData[attribute] = faker.helpers.arrayElement(ratingAnswers); } historicalData.date = faker.date.past(); historicalData.relatedEntity = child.getId(); From 82ab22c5831ac33de4c753c79d54640f80797a98 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Thu, 12 Sep 2024 13:42:57 +0200 Subject: [PATCH 04/11] ci: code coverage upload with old GIT_BRANCH env reverted to the env that we used before the redesign of the CI jobs --- .../pull-request-update-or-push-tag.yml | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pull-request-update-or-push-tag.yml b/.github/workflows/pull-request-update-or-push-tag.yml index 887dbb918b..6810bffc5c 100644 --- a/.github/workflows/pull-request-update-or-push-tag.yml +++ b/.github/workflows/pull-request-update-or-push-tag.yml @@ -14,19 +14,12 @@ jobs: runs-on: ubuntu-latest env: GIT_COMMIT_SHA: ${{ github.sha }} + GIT_BRANCH: ${{ github.head_ref }} CC_TEST_REPORTER_ID: ${{ secrets.CODE_CLIMATE_ID }} steps: - name: Checkout repository files uses: actions/checkout@v4 - - name: Extract tag (release) - if: ${{ github.event.ref != '' }} - run: echo "GIT_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Extract tag (pull request) - if: ${{ github.event.ref == '' }} - run: echo "GIT_BRANCH=pr-${{ github.event.number }}" >> $GITHUB_ENV - - name: Install Code Climate Test Reporter run: | curl -Lo cc-test-reporter https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 @@ -118,19 +111,12 @@ jobs: - test env: GIT_COMMIT_SHA: ${{ github.sha }} + GIT_BRANCH: ${{ github.head_ref }} CC_TEST_REPORTER_ID: ${{ secrets.CODE_CLIMATE_ID }} steps: - name: Checkout repository files uses: actions/checkout@v4 - - name: Extract tag (release) - if: ${{ github.event.ref != '' }} - run: echo "GIT_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Extract tag (pull request) - if: ${{ github.event.ref == '' }} - run: echo "GIT_BRANCH=pr-${{ github.event.number }}" >> $GITHUB_ENV - - name: Download coverage reports uses: actions/download-artifact@v4 with: From 4a0e252c4cbf0ddcabeb81c5b1f05e86a83a2544 Mon Sep 17 00:00:00 2001 From: Abhishek Negi <108608673+Abhinegi2@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:39:07 +0530 Subject: [PATCH 05/11] feat: bulk edit - update a field for multiple entities at once (#2565) closes #2291 --- .../entity-list/entity-list.component.html | 9 ++ .../entity-list/entity-list.component.ts | 9 ++ .../entity-actions/entity-actions.service.ts | 2 +- .../entity-bulk-edit.component.html | 43 +++++ .../entity-bulk-edit.component.scss | 7 + .../entity-bulk-edit.component.spec.ts | 91 +++++++++++ .../entity-bulk-edit.component.ts | 147 ++++++++++++++++++ .../entity-actions/entity-edit.service.ts | 91 +++++++++++ .../todos/todo-list/todo-list.component.ts | 3 + src/assets/locale/messages.de.xlf | 110 +++++++++---- src/assets/locale/messages.fr.xlf | 108 +++++++++---- src/assets/locale/messages.it.xlf | 108 +++++++++---- src/assets/locale/messages.xlf | 82 ++++++++-- 13 files changed, 714 insertions(+), 96 deletions(-) create mode 100644 src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html create mode 100644 src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.scss create mode 100644 src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.spec.ts create mode 100644 src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.ts create mode 100644 src/app/core/entity/entity-actions/entity-edit.service.ts diff --git a/src/app/core/entity-list/entity-list/entity-list.component.html b/src/app/core/entity-list/entity-list/entity-list.component.html index e6c4fec113..66a77601ad 100644 --- a/src/app/core/entity-list/entity-list/entity-list.component.html +++ b/src/app/core/entity-list/entity-list/entity-list.component.html @@ -258,6 +258,15 @@

{{ title }}

matTooltip="Select rows for an action on multiple records" i18n-matTooltip > + + + diff --git a/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.scss b/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.scss new file mode 100644 index 0000000000..765ea84c5e --- /dev/null +++ b/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.scss @@ -0,0 +1,7 @@ +.entity-form-cell { + mat-form-field, + ::ng-deep .mat-mdc-form-field + { + width: 100%; + } +} diff --git a/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.spec.ts b/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.spec.ts new file mode 100644 index 0000000000..aab0e4378a --- /dev/null +++ b/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.spec.ts @@ -0,0 +1,91 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { EntityBulkEditComponent } from "./entity-bulk-edit.component"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; +import { NoopAnimationsModule } from "@angular/platform-browser/animations"; +import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; +import { AdminEntityService } from "app/core/admin/admin-entity.service"; +import { EntityFormService } from "app/core/common-components/entity-form/entity-form.service"; + +describe("EntityBulkEditComponent", () => { + let component: EntityBulkEditComponent; + let fixture: ComponentFixture>; + let mockDialogRef: jasmine.SpyObj>>; + let mockEntityFormService: jasmine.SpyObj; + + const mockEntityConstructor = { + schema: new Map([ + ["name", { label: "foo" }], + ["gender", { label: "Male" }], + ]), + }; + + const mockEntityData = { + getConstructor: () => mockEntityConstructor, + formData: { + name: "Value 1", + gender: "Value 2", + }, + }; + + beforeEach(async () => { + mockDialogRef = jasmine.createSpyObj("MatDialogRef", ["close"]); + mockEntityFormService = jasmine.createSpyObj("EntityFormService", [ + "createEntityForm", + "extendFormFieldConfig", + ]); + + await TestBed.configureTestingModule({ + imports: [ + EntityBulkEditComponent, + FontAwesomeTestingModule, + NoopAnimationsModule, + ReactiveFormsModule, + ], + providers: [ + { + provide: MAT_DIALOG_DATA, + useValue: { + entitiesToEdit: [mockEntityData], + entityConstructor: mockEntityConstructor, + }, + }, + { provide: MatDialogRef, useValue: mockDialogRef }, + { provide: EntityFormService, useValue: mockEntityFormService }, + FormBuilder, + AdminEntityService, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(EntityBulkEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create the component", () => { + expect(component).toBeTruthy(); + }); + + it("should initialize selectedField with proper values", () => { + component.selectedField = { id: "foo", label: "Test Label" }; + + component.ngOnInit(); + expect(component.selectedFieldFormControl.value).toBe(""); + }); + + it("should fetch and populate entity fields", () => { + component.fetchEntityFieldsData(); + + expect(component.entityFields.length).toBe(2); + expect(component.entityFields[0].key).toBe("name"); + expect(component.entityFields[0].label).toBe("foo"); + }); + + it("should not save if the form is invalid", () => { + component.selectedFieldFormControl.setValue(""); + + component.save(); + + expect(mockDialogRef.close).not.toHaveBeenCalled(); + }); +}); diff --git a/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.ts b/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.ts new file mode 100644 index 0000000000..012d7e424a --- /dev/null +++ b/src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.ts @@ -0,0 +1,147 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { + MAT_DIALOG_DATA, + MatDialogModule, + MatDialogRef, +} from "@angular/material/dialog"; +import { MatButtonModule } from "@angular/material/button"; +import { DialogCloseComponent } from "app/core/common-components/dialog-close/dialog-close.component"; +import { MatInputModule } from "@angular/material/input"; +import { ErrorHintComponent } from "app/core/common-components/error-hint/error-hint.component"; +import { EntityFieldEditComponent } from "app/core/common-components/entity-field-edit/entity-field-edit.component"; +import { + FormControl, + FormsModule, + ReactiveFormsModule, + Validators, +} from "@angular/forms"; +import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { FormFieldConfig } from "app/core/common-components/entity-form/FormConfig"; +import { Entity, EntityConstructor } from "../../model/entity"; +import { MatOption } from "@angular/material/core"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatSelectModule } from "@angular/material/select"; +import { CommonModule } from "@angular/common"; +import { + EntityForm, + EntityFormService, +} from "app/core/common-components/entity-form/entity-form.service"; + +@Component({ + selector: "app-entity-bulk-edit", + standalone: true, + imports: [ + MatDialogModule, + MatButtonModule, + DialogCloseComponent, + MatInputModule, + ErrorHintComponent, + FormsModule, + ReactiveFormsModule, + FontAwesomeModule, + MatTooltipModule, + MatOption, + MatFormFieldModule, + MatSelectModule, + CommonModule, + EntityFieldEditComponent, + ], + templateUrl: "./entity-bulk-edit.component.html", + styleUrl: "./entity-bulk-edit.component.scss", +}) +export class EntityBulkEditComponent implements OnInit { + entityConstructor: EntityConstructor; + entitiesToEdit: E[]; + + selectedFieldFormControl: FormControl; + fieldValueForm: EntityForm; + + /** + * The available fields of the entity, from which the user can choose. + */ + entityFields: Array<{ key: string; label: string; field: any }> = []; + + entityData: E; + showValueForm: boolean = false; + selectedField: FormFieldConfig; + + constructor( + @Inject(MAT_DIALOG_DATA) + data: { + entitiesToEdit: E[]; + entityConstructor: EntityConstructor; + }, + private dialogRef: MatDialogRef, + private entityFormService: EntityFormService, + ) { + this.entityConstructor = data.entityConstructor; + this.entityData = data.entitiesToEdit[0]; + this.entitiesToEdit = data.entitiesToEdit; + } + + ngOnInit(): void { + this.initForm(); + this.fetchEntityFieldsData(); + } + + private initForm() { + this.selectedFieldFormControl = new FormControl("", Validators.required); + } + + fetchEntityFieldsData() { + this.entityFields = Array.from(this.entityConstructor.schema.entries()) + .filter(([key, field]) => field.label) + .map(([key, field]) => ({ + key: key, + label: field.label, + field: field, + })); + } + + async onChangeProperty(fieldId: string) { + this.selectedField = this.entityFormService.extendFormFieldConfig( + fieldId, + this.entityConstructor, + ); + + this.fetchEntityFieldsData(); + + const fieldKeys = this.entityFields.map((item) => item.key); + await this.createEntityForm(fieldKeys); + + this.showValueForm = true; + } + + private async createEntityForm(fieldKeys: string[]) { + this.fieldValueForm = await this.entityFormService.createEntityForm( + fieldKeys, + this.entityData, + ); + + const selectedField = this.selectedFieldFormControl.value; + if (this.fieldValueForm.formGroup.controls[selectedField]) { + this.fieldValueForm.formGroup.controls[selectedField].setValue(""); + } + } + + save() { + this.selectedFieldFormControl.markAsTouched(); + if (this.selectedFieldFormControl.invalid) return; + + const selectedField = this.selectedFieldFormControl.value; + const value = + this.fieldValueForm?.formGroup.controls[selectedField]?.value || ""; + + const returnValue: BulkEditAction = { + selectedField, + value, + }; + this.dialogRef.close(returnValue); + } +} + +export interface BulkEditAction { + selectedField: string; + value: any; +} diff --git a/src/app/core/entity/entity-actions/entity-edit.service.ts b/src/app/core/entity/entity-actions/entity-edit.service.ts new file mode 100644 index 0000000000..ea69f9f2ba --- /dev/null +++ b/src/app/core/entity/entity-actions/entity-edit.service.ts @@ -0,0 +1,91 @@ +import { Injectable } from "@angular/core"; +import { EntityMapperService } from "../entity-mapper/entity-mapper.service"; +import { Entity, EntityConstructor } from "../model/entity"; +import { EntitySchemaService } from "../schema/entity-schema.service"; +import { CascadingEntityAction } from "./cascading-entity-action"; +import { UnsavedChangesService } from "app/core/entity-details/form/unsaved-changes.service"; +import { lastValueFrom } from "rxjs"; +import { + BulkEditAction, + EntityBulkEditComponent, +} from "./entity-bulk-edit/entity-bulk-edit.component"; +import { MatDialog } from "@angular/material/dialog"; +import { EntityActionsService } from "./entity-actions.service"; +import { asArray } from "../../../utils/utils"; + +/** + * Bulk edit fields of multiple entities at once. + */ +@Injectable({ + providedIn: "root", +}) +export class EntityEditService extends CascadingEntityAction { + constructor( + protected override entityMapper: EntityMapperService, + protected override schemaService: EntitySchemaService, + private matDialog: MatDialog, + private entityActionsService: EntityActionsService, + private unsavedChanges: UnsavedChangesService, + ) { + super(entityMapper, schemaService); + } + + /** + * Shows a confirmation dialog to the user + * and edit the entity if the user confirms. + * + * This also triggers a toast message, enabling the user to undo the action. + * + * @param entitiesToEdit The entities to apply a bulk edit to. + * @param entityType + */ + async edit( + entitiesToEdit: E | E[], + entityType: EntityConstructor, + ): Promise { + let entities = asArray(entitiesToEdit); + const dialogRef = this.matDialog.open(EntityBulkEditComponent, { + maxHeight: "90vh", + data: { entityConstructor: entityType, entitiesToEdit: entities }, + }); + const action: BulkEditAction = await lastValueFrom(dialogRef.afterClosed()); + + if (action) { + const result = await this.editEntity(action, entitiesToEdit); + this.entityActionsService.showSnackbarConfirmationWithUndo( + this.entityActionsService.generateMessageForConfirmationWithUndo( + entities, + $localize`:Entity action confirmation message verb:edited`, + ), + result.originalEntities, + ); + } + return true; + } + + async editEntity( + action: BulkEditAction, + entitiesToEdit: E | E[], + ): Promise<{ success: boolean; originalEntities: E[]; newEntities: E[] }> { + if (!action) { + return; + } + + let originalEntities: E[] = Array.isArray(entitiesToEdit) + ? entitiesToEdit + : [entitiesToEdit]; + const newEntities: E[] = originalEntities.map((e) => e.copy()); + + for (const e of newEntities) { + e[action.selectedField] = action.value; + await this.entityMapper.save(e); + } + + this.unsavedChanges.pending = false; + return { + success: true, + originalEntities, + newEntities, + }; + } +} diff --git a/src/app/features/todos/todo-list/todo-list.component.ts b/src/app/features/todos/todo-list/todo-list.component.ts index 03528e59bb..18c78550d8 100644 --- a/src/app/features/todos/todo-list/todo-list.component.ts +++ b/src/app/features/todos/todo-list/todo-list.component.ts @@ -47,6 +47,7 @@ import { EntityActionsService } from "app/core/entity/entity-actions/entity-acti import { AbilityModule } from "@casl/angular"; import { ViewActionsComponent } from "../../../core/common-components/view-actions/view-actions.component"; import { EntityActionsMenuComponent } from "../../../core/entity-details/entity-actions-menu/entity-actions-menu.component"; +import { EntityEditService } from "app/core/entity/entity-actions/entity-edit.service"; @UntilDestroy() @RouteTarget("TodoList") @@ -107,6 +108,7 @@ export class TodoListComponent activatedRoute: ActivatedRoute, entityMapperService: EntityMapperService, entityActionsService: EntityActionsService, + entityEditService: EntityEditService, entities: EntityRegistry, dialog: MatDialog, duplicateRecord: DuplicateRecordService, @@ -122,6 +124,7 @@ export class TodoListComponent dialog, duplicateRecord, entityActionsService, + entityEditService, null, ); } diff --git a/src/assets/locale/messages.de.xlf b/src/assets/locale/messages.de.xlf index c4dad024ce..f2c41e4885 100644 --- a/src/assets/locale/messages.de.xlf +++ b/src/assets/locale/messages.de.xlf @@ -68,6 +68,10 @@ src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html 194 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 41 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 123 @@ -4214,9 +4218,9 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi 258 - - Archive - Archivieren + + Bulk Edit + Bearbeiten src/app/core/entity-list/entity-list/entity-list.component.html 267,269 @@ -4227,44 +4231,57 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi bulk action button + + Archive + Archivieren + bulk action button + + src/app/core/entity-list/entity-list/entity-list.component.html + 276 + + + src/app/core/entity-list/entity-list/entity-list.component.html + 276 + + Anonymize Anonymisieren + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 276,278 + 285 src/app/core/entity-list/entity-list/entity-list.component.html - 276,278 + 285 - bulk action button Delete Löschen + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 285,287 + 294 src/app/core/entity-list/entity-list/entity-list.component.html - 285,287 + 294 - bulk action button Duplicate Duplizieren + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 294,296 + 303 src/app/core/entity-list/entity-list/entity-list.component.html - 294,296 + 303 - bulk action button male @@ -5333,35 +5350,35 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Cancel Abbrechen + Cancel button for forms src/app/core/entity-details/form/form.component.html - 19,21 + 19 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307 src/app/core/import/import-confirm-summary/import-confirm-summary.component.html - 30,32 + 30 src/app/core/import/import/import.component.html - 162,164 + 162 src/app/core/user/user-security/user-security.component.html - 72,74 + 72 src/app/features/todos/recurring-interval/custom-interval/custom-interval.component.html - 35,37 + 35 - Cancel button for forms Edit @@ -6094,6 +6111,32 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi 298 + + Bulk edit + + Bearbeiten von + + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 1,3 + + + + You are about to modify the selected records. This action will apply changes across multiple entries. Make sure that the fields you are updating reflect the correct information for all selected records. If you are unsure about making changes across all these records, review your selection carefully before proceeding or edit the records individually. + Sie sind dabei, alle ausgewählten Datensätze zu bearbeiten. Diese Aktion wendet Änderungen auf mehrere Einträge an. Stellen Sie sicher, dass die Felder, die Sie aktualisieren, die richtigen Informationen für alle ausgewählten Datensätze wiedergeben. Wenn Sie sich nicht sicher sind, ob Sie Änderungen an allen diesen Datensätzen vornehmen sollen, bearbeiten Sie die Datensätze gegegebenfalls stattdessen einzeln. + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 7,13 + + + + Property to update + Feld das bearbeitet wird + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 18 + + Keycloak User could not be deleted Keycloak-Account konnte nicht gelöscht werden @@ -6112,9 +6155,18 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi delete account in keycloak related error dialog + + edited + bearbeitet + + src/app/core/entity/entity-actions/entity-edit.service.ts + 53 + + Entity action confirmation message verb + Undo - Zurück + Rückgängig machen Undo an entity action src/app/core/entity/entity-actions/entity-actions.service.ts @@ -6598,6 +6650,10 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi src/app/core/common-components/confirmation-dialog/confirmation-dialog/confirmation-dialog.component.ts 112 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 42 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 126 @@ -8437,7 +8493,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 206 + 209 @@ -8446,7 +8502,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 172 + 175 @@ -8455,7 +8511,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 177 + 180 @@ -8464,7 +8520,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 182 + 185 @@ -8472,7 +8528,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Alle src/app/features/todos/todo-list/todo-list.component.ts - 185 + 188 @@ -8480,7 +8536,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi fällige Aufgaben src/app/features/todos/todo-list/todo-list.component.ts - 187 + 190 diff --git a/src/assets/locale/messages.fr.xlf b/src/assets/locale/messages.fr.xlf index 47e6bcd545..c8954edac8 100644 --- a/src/assets/locale/messages.fr.xlf +++ b/src/assets/locale/messages.fr.xlf @@ -316,6 +316,10 @@ src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html 194 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 41 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 123 @@ -1564,9 +1568,9 @@ 258 - - Archive - Archive + + Bulk Edit + Bulk Edit src/app/core/entity-list/entity-list/entity-list.component.html 267,269 @@ -1577,44 +1581,57 @@ bulk action button + + Archive + Archive + bulk action button + + src/app/core/entity-list/entity-list/entity-list.component.html + 276 + + + src/app/core/entity-list/entity-list/entity-list.component.html + 276 + + Anonymize Anonymize + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 276,278 + 285 src/app/core/entity-list/entity-list/entity-list.component.html - 276,278 + 285 - bulk action button Delete Supprimer + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 285,287 + 294 src/app/core/entity-list/entity-list/entity-list.component.html - 285,287 + 294 - bulk action button Duplicate Duplicate + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 294,296 + 303 src/app/core/entity-list/entity-list/entity-list.component.html - 294,296 + 303 - bulk action button male @@ -4983,35 +5000,35 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Cancel Annuler + Cancel button for forms src/app/core/entity-details/form/form.component.html - 19,21 + 19 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307 src/app/core/import/import-confirm-summary/import-confirm-summary.component.html - 30,32 + 30 src/app/core/import/import/import.component.html - 162,164 + 162 src/app/core/user/user-security/user-security.component.html - 72,74 + 72 src/app/features/todos/recurring-interval/custom-interval/custom-interval.component.html - 35,37 + 35 - Cancel button for forms Edit @@ -5212,6 +5229,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/core/common-components/confirmation-dialog/confirmation-dialog/confirmation-dialog.component.ts 112 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 42 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 126 @@ -6078,6 +6099,32 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit 298 + + Bulk edit + + Bulk edit + + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 1,3 + + + + You are about to modify the selected records. This action will apply changes across multiple entries. Make sure that the fields you are updating reflect the correct information for all selected records. If you are unsure about making changes across all these records, review your selection carefully before proceeding or edit the records individually. + You are about to modify the selected records. This action will apply changes across multiple entries. Make sure that the fields you are updating reflect the correct information for all selected records. If you are unsure about making changes across all these records, review your selection carefully before proceeding or edit the records individually. + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 7,13 + + + + Property to update + Property to update + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 18 + + Keycloak User could not be deleted Keycloak User could not be deleted @@ -6096,6 +6143,15 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit delete account in keycloak related error dialog + + edited + edited + + src/app/core/entity/entity-actions/entity-edit.service.ts + 53 + + Entity action confirmation message verb + Undo Annuler l'action @@ -8501,7 +8557,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 206 + 209 @@ -8510,7 +8566,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 172 + 175 @@ -8519,7 +8575,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 177 + 180 @@ -8528,7 +8584,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 182 + 185 @@ -8536,7 +8592,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Any src/app/features/todos/todo-list/todo-list.component.ts - 185 + 188 @@ -8544,7 +8600,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Tasks due src/app/features/todos/todo-list/todo-list.component.ts - 187 + 190 diff --git a/src/assets/locale/messages.it.xlf b/src/assets/locale/messages.it.xlf index 7a890cba84..7206830b4e 100644 --- a/src/assets/locale/messages.it.xlf +++ b/src/assets/locale/messages.it.xlf @@ -138,6 +138,10 @@ src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html 194 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 41 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 123 @@ -1774,9 +1778,9 @@ 258 - - Archive - Archive + + Bulk Edit + Bulk Edit src/app/core/entity-list/entity-list/entity-list.component.html 267,269 @@ -1787,44 +1791,57 @@ bulk action button + + Archive + Archive + bulk action button + + src/app/core/entity-list/entity-list/entity-list.component.html + 276 + + + src/app/core/entity-list/entity-list/entity-list.component.html + 276 + + Anonymize Anonymize + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 276,278 + 285 src/app/core/entity-list/entity-list/entity-list.component.html - 276,278 + 285 - bulk action button Delete Elimina + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 285,287 + 294 src/app/core/entity-list/entity-list/entity-list.component.html - 285,287 + 294 - bulk action button Duplicate Duplicate + bulk action button src/app/core/entity-list/entity-list/entity-list.component.html - 294,296 + 303 src/app/core/entity-list/entity-list/entity-list.component.html - 294,296 + 303 - bulk action button male @@ -4283,35 +4300,35 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Cancel Cancella + Cancel button for forms src/app/core/entity-details/form/form.component.html - 19,21 + 19 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307 src/app/core/import/import-confirm-summary/import-confirm-summary.component.html - 30,32 + 30 src/app/core/import/import/import.component.html - 162,164 + 162 src/app/core/user/user-security/user-security.component.html - 72,74 + 72 src/app/features/todos/recurring-interval/custom-interval/custom-interval.component.html - 35,37 + 35 - Cancel button for forms Edit @@ -4844,6 +4861,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/core/common-components/confirmation-dialog/confirmation-dialog/confirmation-dialog.component.ts 112 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 42 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 126 @@ -5682,6 +5703,32 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit 298 + + Bulk edit + + Bulk edit + + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 1,3 + + + + You are about to modify the selected records. This action will apply changes across multiple entries. Make sure that the fields you are updating reflect the correct information for all selected records. If you are unsure about making changes across all these records, review your selection carefully before proceeding or edit the records individually. + You are about to modify the selected records. This action will apply changes across multiple entries. Make sure that the fields you are updating reflect the correct information for all selected records. If you are unsure about making changes across all these records, review your selection carefully before proceeding or edit the records individually. + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 7,13 + + + + Property to update + Property to update + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 18 + + Keycloak User could not be deleted Keycloak User could not be deleted @@ -5700,6 +5747,15 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit delete account in keycloak related error dialog + + edited + edited + + src/app/core/entity/entity-actions/entity-edit.service.ts + 53 + + Entity action confirmation message verb + Undo Annullare @@ -8598,7 +8654,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 206 + 209 @@ -8607,7 +8663,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 172 + 175 @@ -8616,7 +8672,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 177 + 180 @@ -8625,7 +8681,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Filter-option for todos src/app/features/todos/todo-list/todo-list.component.ts - 182 + 185 @@ -8633,7 +8689,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Any src/app/features/todos/todo-list/todo-list.component.ts - 185 + 188 @@ -8641,7 +8697,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Tasks due src/app/features/todos/todo-list/todo-list.component.ts - 187 + 190 diff --git a/src/assets/locale/messages.xlf b/src/assets/locale/messages.xlf index fcadb4fcb4..1dbe62e760 100644 --- a/src/assets/locale/messages.xlf +++ b/src/assets/locale/messages.xlf @@ -127,6 +127,10 @@ src/app/core/admin/admin-entity-details/admin-entity-field/admin-entity-field.component.html 194 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 41 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 123 @@ -1703,6 +1707,10 @@ src/app/core/common-components/confirmation-dialog/confirmation-dialog/confirmation-dialog.component.ts 112 + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 42 + src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.html 126 @@ -4553,11 +4561,11 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307,309 src/app/core/entity-list/entity-list/entity-list.component.html - 298,300 + 307,309 src/app/core/import/import-confirm-summary/import-confirm-summary.component.html @@ -4796,8 +4804,8 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit 258 - - Archive + + Bulk Edit src/app/core/entity-list/entity-list/entity-list.component.html 267,269 @@ -4808,8 +4816,8 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit bulk action button - - Anonymize + + Archive src/app/core/entity-list/entity-list/entity-list.component.html 276,278 @@ -4820,8 +4828,8 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit bulk action button - - Delete + + Anonymize src/app/core/entity-list/entity-list/entity-list.component.html 285,287 @@ -4832,8 +4840,8 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit bulk action button - - Duplicate + + Delete src/app/core/entity-list/entity-list/entity-list.component.html 294,296 @@ -4844,6 +4852,18 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit bulk action button + + Duplicate + + src/app/core/entity-list/entity-list/entity-list.component.html + 303,305 + + + src/app/core/entity-list/entity-list/entity-list.component.html + 303,305 + + bulk action button + Preparing data (Indexing) @@ -5075,6 +5095,28 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Entity action confirmation message + + Bulk edit + + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 1,3 + + + + You are about to modify the selected records. This action will apply changes across multiple entries. Make sure that the fields you are updating reflect the correct information for all selected records. If you are unsure about making changes across all these records, review your selection carefully before proceeding or edit the records individually. + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 7,13 + + + + Property to update + + src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html + 18 + + Keycloak User could not be deleted @@ -5091,6 +5133,14 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit delete account in keycloak related error dialog + + edited + + src/app/core/entity/entity-actions/entity-edit.service.ts + 53 + + Entity action confirmation message verb + [anonymized ] @@ -7541,7 +7591,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Overdue src/app/features/todos/todo-list/todo-list.component.ts - 172 + 175 Filter-option for todos @@ -7549,7 +7599,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Completed src/app/features/todos/todo-list/todo-list.component.ts - 177 + 180 Filter-option for todos @@ -7557,7 +7607,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit All Open src/app/features/todos/todo-list/todo-list.component.ts - 182 + 185 Filter-option for todos @@ -7565,21 +7615,21 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Any src/app/features/todos/todo-list/todo-list.component.ts - 185 + 188 Tasks due src/app/features/todos/todo-list/todo-list.component.ts - 187 + 190 Currently Active src/app/features/todos/todo-list/todo-list.component.ts - 206 + 209 Filter-option for todos From 27f6f44e2dfc7386fbd7206e7c30a23daf56d96d Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 23 Sep 2024 13:02:16 +0200 Subject: [PATCH 06/11] feat: template export support (pdf generator) --- proxy.conf.json | 2 +- src/app/app.module.ts | 2 + .../edit-configurable-enum.component.spec.ts | 37 ++- .../entity-block/entity-block.component.html | 12 +- .../entity-select.component.html | 2 +- .../entity-select/entity-select.component.ts | 15 +- .../config/dynamic-routing/router.service.ts | 14 +- src/app/core/core-components.ts | 7 + .../default-datatype/edit-component.spec.ts | 5 +- .../edit-entity-type-dropdown.component.html | 11 + ...dit-entity-type-dropdown.component.spec.ts | 28 +++ .../edit-entity-type-dropdown.component.ts | 55 +++++ .../download-service/download.service.ts | 13 +- .../export-data.directive.ts | 9 +- src/app/features/file/couchdb-file.service.ts | 94 ++------ .../file/edit-file/edit-file.component.ts | 34 ++- src/app/features/file/file.service.ts | 72 +++++- src/app/features/file/mock-file.service.ts | 6 +- .../template-export-api.service.spec.ts | 169 ++++++++++++++ .../template-export-api.service.ts | 160 +++++++++++++ ...dit-template-export-file.component.spec.ts | 31 +++ .../edit-template-export-file.component.ts | 32 +++ .../template-export-file.datatype.spec.ts | 6 + .../template-export-file.datatype.ts | 16 ++ ...ate-export-selection-dialog.component.html | 45 ++++ ...ate-export-selection-dialog.component.scss | 12 + ...-export-selection-dialog.component.spec.ts | 146 ++++++++++++ ...plate-export-selection-dialog.component.ts | 91 ++++++++ .../template-export.service.spec.ts | 34 +++ .../template-export.service.ts | 26 +++ .../template-export/template-export.entity.ts | 88 ++++++++ .../template-export/template-export.module.ts | 130 +++++++++++ src/assets/locale/messages.de.xlf | 212 +++++++++++++++++- src/assets/locale/messages.fr.xlf | 212 +++++++++++++++++- src/assets/locale/messages.it.xlf | 212 +++++++++++++++++- src/assets/locale/messages.xlf | 194 +++++++++++++++- 36 files changed, 2085 insertions(+), 149 deletions(-) create mode 100644 src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.html create mode 100644 src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.spec.ts create mode 100644 src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.ts create mode 100644 src/app/features/template-export/template-export-api/template-export-api.service.spec.ts create mode 100644 src/app/features/template-export/template-export-api/template-export-api.service.ts create mode 100644 src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.spec.ts create mode 100644 src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.ts create mode 100644 src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.spec.ts create mode 100644 src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.ts create mode 100644 src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html create mode 100644 src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.scss create mode 100644 src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.spec.ts create mode 100644 src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts create mode 100644 src/app/features/template-export/template-export-service/template-export.service.spec.ts create mode 100644 src/app/features/template-export/template-export-service/template-export.service.ts create mode 100644 src/app/features/template-export/template-export.entity.ts create mode 100644 src/app/features/template-export/template-export.module.ts diff --git a/proxy.conf.json b/proxy.conf.json index 4237f1a41e..b53b95efd7 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -9,7 +9,7 @@ } }, "/query": { - "target": "http://localhost:3000", + "target": "http://localhost:9000", "secure": true, "logLevel": "debug", "changeOrigin": true, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 51bf99fc41..b544dec3b2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -87,6 +87,7 @@ import { LoginStateSubject } from "./core/session/session-type"; import { AdminModule } from "./core/admin/admin.module"; import { Logging } from "./core/logging/logging.service"; import { APP_INITIALIZER_DEMO_DATA } from "./core/demo-data/demo-data.app-initializer"; +import { TemplateExportModule } from "./features/template-export/template-export.module"; /** * Main entry point of the application. @@ -130,6 +131,7 @@ import { APP_INITIALIZER_DEMO_DATA } from "./core/demo-data/demo-data.app-initia ReportingModule, TodosModule, AdminModule, + TemplateExportModule, // top level component UiComponent, // Global Angular Material modules diff --git a/src/app/core/basic-datatypes/configurable-enum/edit-configurable-enum/edit-configurable-enum.component.spec.ts b/src/app/core/basic-datatypes/configurable-enum/edit-configurable-enum/edit-configurable-enum.component.spec.ts index 4343275273..ca0266e63e 100644 --- a/src/app/core/basic-datatypes/configurable-enum/edit-configurable-enum/edit-configurable-enum.component.spec.ts +++ b/src/app/core/basic-datatypes/configurable-enum/edit-configurable-enum/edit-configurable-enum.component.spec.ts @@ -1,25 +1,21 @@ -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { EditConfigurableEnumComponent } from "./edit-configurable-enum.component"; -import { FormControl, FormGroup } from "@angular/forms"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; -import { EntitySchemaField } from "../../../entity/schema/entity-schema-field"; -import { Entity } from "../../../entity/model/entity"; +import { setupEditComponent } from "../../../entity/default-datatype/edit-component.spec"; describe("EditConfigurableEnumComponent", () => { let component: EditConfigurableEnumComponent; let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ imports: [EditConfigurableEnumComponent, MockedTestingModule.withState()], }).compileComponents(); - })); - beforeEach(() => { fixture = TestBed.createComponent(EditConfigurableEnumComponent); component = fixture.componentInstance; - initWithSchema({ additional: "some-id" }); + setupEditComponent(component, "test", { additional: "some-id" }); fixture.detectChanges(); }); @@ -28,26 +24,21 @@ describe("EditConfigurableEnumComponent", () => { }); it("should extract the enum ID", () => { - initWithSchema({ additional: "some-id" }); + setupEditComponent(component, "test", { additional: "some-id" }); + component.ngOnInit(); expect(component.enumId).toBe("some-id"); - - initWithSchema({ dataType: "array", additional: "other-id" }); - expect(component.enumId).toBe("other-id"); }); it("should detect multi selection mode", () => { - initWithSchema({ additional: "some-id" }); + setupEditComponent(component, "test", { additional: "some-id" }); + component.ngOnInit(); expect(component.multi).toBeFalsy(); - initWithSchema({ isArray: true, additional: "some-id" }); + setupEditComponent(component, "test", { + isArray: true, + additional: "some-id", + }); + component.ngOnInit(); expect(component.multi).toBeTrue(); }); - - function initWithSchema(schema: EntitySchemaField) { - const fromGroup = new FormGroup({ test: new FormControl() }); - component.formControl = fromGroup.get("test") as FormControl; - component.formFieldConfig = { id: "test", ...schema }; // EditComponents are ensure to receive fully extended formFieldConfig - component.entity = new Entity(); - component.ngOnInit(); - } }); diff --git a/src/app/core/basic-datatypes/entity/entity-block/entity-block.component.html b/src/app/core/basic-datatypes/entity/entity-block/entity-block.component.html index 3e2f66f7eb..0891fcf1d4 100644 --- a/src/app/core/basic-datatypes/entity/entity-block/entity-block.component.html +++ b/src/app/core/basic-datatypes/entity/entity-block/entity-block.component.html @@ -20,11 +20,13 @@
- + @if (entityBlockConfig?.image) { + + }
diff --git a/src/app/core/common-components/entity-select/entity-select.component.html b/src/app/core/common-components/entity-select/entity-select.component.html index 330dc5846a..9bbed8c142 100644 --- a/src/app/core/common-components/entity-select/entity-select.component.html +++ b/src/app/core/common-components/entity-select/entity-select.component.html @@ -11,7 +11,7 @@ [display]="showEntities ? 'chips' : 'none'" [options]="availableOptions | async" [placeholder]="(loading | async) ? loadingPlaceholder : placeholder" - [createOption]="createNewEntity" + [createOption]="disableCreateNew ? null : createNewEntity" > (type)), - ); + entities.push(...(await this.entityMapperService.loadType(type))); } - this.allEntities.sort((a, b) => a.toString().localeCompare(b.toString())); + this.allEntities = entities + .filter((e) => this.additionalFilter(e)) + .sort((a, b) => a.toString().localeCompare(b.toString())); await this.updateAvailableOptions(); diff --git a/src/app/core/config/dynamic-routing/router.service.ts b/src/app/core/config/dynamic-routing/router.service.ts index c365db7516..d5cc307c39 100644 --- a/src/app/core/config/dynamic-routing/router.service.ts +++ b/src/app/core/config/dynamic-routing/router.service.ts @@ -34,6 +34,14 @@ export class RouterService { this.reloadRouting(viewConfigs, this.router.config); } + /** + * Add additional routes to the existing routing configuration, e.g. registering view configs for a new module. + * @param viewConfigs + */ + addRoutes(viewConfigs: ViewConfig[]) { + this.reloadRouting(viewConfigs, this.router.config); + } + /** * Reset the routing config and reload it from the global config. * @@ -71,7 +79,11 @@ export class RouterService { } private createRoute(view: ViewConfig, additionalRoutes: Route[]) { - const path = view._id.substring(PREFIX_VIEW_CONFIG.length); + const path = view._id + .substring(PREFIX_VIEW_CONFIG.length) + // remove leading slash if present + .replace(/^\//, ""); + const existingRoute = additionalRoutes.find((r) => r.path === path); if (existingRoute) { diff --git a/src/app/core/core-components.ts b/src/app/core/core-components.ts index b8a3c77a04..a28b54a489 100644 --- a/src/app/core/core-components.ts +++ b/src/app/core/core-components.ts @@ -240,4 +240,11 @@ export const coreComponents: ComponentTuple[] = [ "./entity-details/related-entities-with-summary/related-entities-with-summary.component" ).then((c) => c.RelatedEntitiesWithSummaryComponent), ], + [ + "EditEntityTypeDropdown", + () => + import( + "./entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component" + ).then((c) => c.EditEntityTypeDropdownComponent), + ], ]; diff --git a/src/app/core/entity/default-datatype/edit-component.spec.ts b/src/app/core/entity/default-datatype/edit-component.spec.ts index 45bb9e5fcc..7e06e283c9 100644 --- a/src/app/core/entity/default-datatype/edit-component.spec.ts +++ b/src/app/core/entity/default-datatype/edit-component.spec.ts @@ -1,22 +1,25 @@ import { EditComponent } from "./edit-component"; import { UntypedFormControl, UntypedFormGroup } from "@angular/forms"; import { Entity } from "../model/entity"; +import { EntitySchemaField } from "../schema/entity-schema-field"; /** * A simple helper class that sets up a EditComponent with the required FormGroup * @param component that extends EditComponent * @param propertyName (optional) the name of the property for which the edit component is created + * @param schema (optional) additional schema information for the entity field */ export function setupEditComponent( component: EditComponent, propertyName = "testProperty", + schema: EntitySchemaField = {}, ): UntypedFormGroup { const formControl = new UntypedFormControl(); const fromGroupConfig = {}; fromGroupConfig[propertyName] = formControl; const formGroup = new UntypedFormGroup(fromGroupConfig); component.formControl = formControl; - component.formFieldConfig = { id: propertyName }; + component.formFieldConfig = { id: propertyName, ...schema }; component.entity = new Entity(); return formGroup; } diff --git a/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.html b/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.html new file mode 100644 index 0000000000..a0e43a1854 --- /dev/null +++ b/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.html @@ -0,0 +1,11 @@ + + {{ label }} + + + diff --git a/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.spec.ts b/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.spec.ts new file mode 100644 index 0000000000..b62025938f --- /dev/null +++ b/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.spec.ts @@ -0,0 +1,28 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { EditEntityTypeDropdownComponent } from "./edit-entity-type-dropdown.component"; +import { MockedTestingModule } from "../../../utils/mocked-testing.module"; +import { setupEditComponent } from "../default-datatype/edit-component.spec"; + +describe("EditConfigurableEnumComponent", () => { + let component: EditEntityTypeDropdownComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + EditEntityTypeDropdownComponent, + MockedTestingModule.withState(), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(EditEntityTypeDropdownComponent); + component = fixture.componentInstance; + setupEditComponent(component); + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.ts b/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.ts new file mode 100644 index 0000000000..70addd9171 --- /dev/null +++ b/src/app/core/entity/edit-entity-type-dropdown/edit-entity-type-dropdown.component.ts @@ -0,0 +1,55 @@ +import { Component, OnInit } from "@angular/core"; +import { EditComponent } from "../default-datatype/edit-component"; +import { DynamicComponent } from "../../config/dynamic-components/dynamic-component.decorator"; +import { BasicAutocompleteComponent } from "../../common-components/basic-autocomplete/basic-autocomplete.component"; +import { FaIconComponent } from "@fortawesome/angular-fontawesome"; +import { MatFormField, MatLabel } from "@angular/material/form-field"; +import { MatTooltip } from "@angular/material/tooltip"; +import { NgIf } from "@angular/common"; +import { ReactiveFormsModule } from "@angular/forms"; +import { MatInput } from "@angular/material/input"; +import { EntityConstructor } from "../model/entity"; +import { EntityRegistry } from "../database-entity.decorator"; + +/** + * Edit component for selecting an entity type from a dropdown. + */ +@DynamicComponent("EditEntityTypeDropdown") +@Component({ + selector: "app-edit-entity-type-dropdown", + templateUrl: "./edit-entity-type-dropdown.component.html", + imports: [ + BasicAutocompleteComponent, + FaIconComponent, + MatFormField, + MatLabel, + MatTooltip, + NgIf, + ReactiveFormsModule, + MatInput, + ], + standalone: true, +}) +export class EditEntityTypeDropdownComponent + extends EditComponent + implements OnInit +{ + multi = false; + + entityTypes: EntityConstructor[]; + optionToLabel = (option: EntityConstructor) => option.label; + optionToId = (option: EntityConstructor) => option.ENTITY_TYPE; + + constructor(private entityRegistry: EntityRegistry) { + super(); + } + + override ngOnInit() { + super.ngOnInit(); + this.multi = this.formFieldConfig.isArray; + + this.entityTypes = this.entityRegistry + .getEntityTypes(true) + .map(({ value }) => value); + } +} diff --git a/src/app/core/export/download-service/download.service.ts b/src/app/core/export/download-service/download.service.ts index e80c45354c..f468c2a506 100644 --- a/src/app/core/export/download-service/download.service.ts +++ b/src/app/core/export/download-service/download.service.ts @@ -1,6 +1,5 @@ import { Injectable } from "@angular/core"; import { ExportColumnConfig } from "../data-transformation-service/export-column-config"; -import { ExportDataFormat } from "../export-data-directive/export-data.directive"; import { Logging } from "../../logging/logging.service"; import { DataTransformationService } from "../data-transformation-service/data-transformation.service"; import { transformToReadableFormat } from "../../common-components/entities-table/value-accessor/value-accessor"; @@ -9,6 +8,8 @@ import { Entity, EntityConstructor } from "app/core/entity/model/entity"; import { EntityDatatype } from "app/core/basic-datatypes/entity/entity.datatype"; import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; +export type FileDownloadFormat = "csv" | "json" | "pdf"; + /** * This service allows to start a download process from the browser. * Depending on the browser and the setting this might open a popup or directly download the file. @@ -35,7 +36,7 @@ export class DownloadService { */ async triggerDownload( data: any, - format: ExportDataFormat, + format: FileDownloadFormat, filename: string, exportConfig?: ExportColumnConfig[], ) { @@ -44,14 +45,16 @@ export class DownloadService { format, exportConfig, ); - const filenameWithExtension = filename + "." + format.toLowerCase(); + const filenameWithExtension = filename.endsWith("." + format) + ? filename + : filename + "." + format; const link = this.createDownloadLink(blobData, filenameWithExtension); link.click(); } private async getFormattedBlobData( data: any, - format: ExportDataFormat, + format: FileDownloadFormat, exportConfig?: ExportColumnConfig[], ): Promise { let result = ""; @@ -70,6 +73,8 @@ export class DownloadService { case "csv": result = await this.createCsv(data); return new Blob([result], { type: "text/csv" }); + case "pdf": + return new Blob([data], { type: "application/pdf" }); default: Logging.warn(`Not supported format: ${format}`); return new Blob([""]); diff --git a/src/app/core/export/export-data-directive/export-data.directive.ts b/src/app/core/export/export-data-directive/export-data.directive.ts index 0b0ac5e764..96bd9352bd 100644 --- a/src/app/core/export/export-data-directive/export-data.directive.ts +++ b/src/app/core/export/export-data-directive/export-data.directive.ts @@ -1,8 +1,9 @@ import { Directive, HostListener, Input } from "@angular/core"; import { ExportColumnConfig } from "../data-transformation-service/export-column-config"; -import { DownloadService } from "../download-service/download.service"; - -export type ExportDataFormat = "csv" | "json"; +import { + DownloadService, + FileDownloadFormat, +} from "../download-service/download.service"; /** * A directive that can be attached to a html element, commonly a button. @@ -27,7 +28,7 @@ export class ExportDataDirective { @Input("appExportData") data: any = []; /** What kind of data should be export? Currently implemented are 'json', 'csv' */ - @Input() format: ExportDataFormat = "csv"; + @Input() format: FileDownloadFormat = "csv"; /** filename for the download of the exported data */ @Input() filename: string = "exportedData"; diff --git a/src/app/features/file/couchdb-file.service.ts b/src/app/features/file/couchdb-file.service.ts index 7c24d34984..d9d27565f0 100644 --- a/src/app/features/file/couchdb-file.service.ts +++ b/src/app/features/file/couchdb-file.service.ts @@ -1,29 +1,17 @@ import { Inject, Injectable } from "@angular/core"; -import { - HttpClient, - HttpEvent, - HttpEventType, - HttpProgressEvent, - HttpResponse, - HttpStatusCode, -} from "@angular/common/http"; +import { HttpStatusCode } from "@angular/common/http"; import { catchError, concatMap, - filter, last, map, shareReplay, tap, } from "rxjs/operators"; import { from, Observable, of, throwError } from "rxjs"; -import { MatDialog } from "@angular/material/dialog"; -import { ShowFileComponent } from "./show-file/show-file.component"; import { Entity } from "../../core/entity/model/entity"; import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; import { FileService } from "./file.service"; -import { MatSnackBar } from "@angular/material/snack-bar"; -import { ProgressComponent } from "./progress/progress.component"; import { EntityRegistry } from "../../core/entity/database-entity.decorator"; import { Logging } from "../../core/logging/logging.service"; import { ObservableQueue } from "./observable-queue/observable-queue"; @@ -49,9 +37,6 @@ export class CouchdbFileService extends FileService { constructor( private sanitizer: DomSanitizer, - private http: HttpClient, - private dialog: MatDialog, - private snackbar: MatSnackBar, private syncService: SyncService, entityMapper: EntityMapperService, entities: EntityRegistry, @@ -83,7 +68,7 @@ export class CouchdbFileService extends FileService { return this.ensureDocIsSynced().pipe( concatMap(() => this.getAttachmentsDocument(attachmentPath)), concatMap(({ _rev }) => - this.http.put(`${attachmentPath}/${property}?rev=${_rev}`, file, { + this.httpClient.put(`${attachmentPath}/${property}?rev=${_rev}`, file, { headers: { "ngsw-bypass": "" }, reportProgress: true, observe: "events", @@ -106,16 +91,18 @@ export class CouchdbFileService extends FileService { private getAttachmentsDocument( attachmentPath: string, ): Observable<{ _rev: string }> { - return this.http.get<{ _id: string; _rev: string }>(attachmentPath).pipe( - catchError((err) => { - if (err.status === HttpStatusCode.NotFound) { - return this.http - .put<{ rev: string }>(attachmentPath, {}) - .pipe(map((res) => ({ _rev: res.rev }))); - } - throw err; - }), - ); + return this.httpClient + .get<{ _id: string; _rev: string }>(attachmentPath) + .pipe( + catchError((err) => { + if (err.status === HttpStatusCode.NotFound) { + return this.httpClient + .put<{ rev: string }>(attachmentPath, {}) + .pipe(map((res) => ({ _rev: res.rev }))); + } + throw err; + }), + ); } removeFile(entity: Entity, property: string) { @@ -128,11 +115,11 @@ export class CouchdbFileService extends FileService { private runFileRemoval(entity: Entity, property: string) { const path = `${entity.getId()}/${property}`; - return this.http + return this.httpClient .get<{ _rev: string }>(`${this.attachmentsUrl}/${entity.getId()}`) .pipe( concatMap(({ _rev }) => - this.http.delete(`${this.attachmentsUrl}/${path}?rev=${_rev}`), + this.httpClient.delete(`${this.attachmentsUrl}/${path}?rev=${_rev}`), ), tap(() => delete this.cache[path]), catchError((err) => { @@ -151,35 +138,17 @@ export class CouchdbFileService extends FileService { private runAllFilesRemoval(entity: Entity) { const attachmentPath = `${this.attachmentsUrl}/${entity.getId()}`; - return this.http + return this.httpClient .get<{ _rev: string }>(attachmentPath) .pipe( concatMap(({ _rev }) => - this.http.delete(`${attachmentPath}?rev=${_rev}`), + this.httpClient.delete(`${attachmentPath}?rev=${_rev}`), ), ); } - showFile(entity: Entity, property: string) { - const obs = this.http - .get(`${this.attachmentsUrl}/${entity.getId()}/${property}`, { - responseType: "blob", - reportProgress: true, - observe: "events", - headers: { "ngsw-bypass": "" }, - }) - .pipe(shareReplay()); - this.reportProgress($localize`Loading "${entity[property]}"`, obs); - obs - .pipe(filter((e) => e.type === HttpEventType.Response)) - .subscribe((e: HttpResponse) => { - const fileURL = URL.createObjectURL(e.body); - const win = window.open(fileURL, "_blank"); - if (!win || win.closed || typeof win.closed == "undefined") { - // When it takes more than a few (2-5) seconds to open the file, the browser might block the popup - this.dialog.open(ShowFileComponent, { data: fileURL }); - } - }); + protected override getShowFileUrl(entity: Entity, property: string): string { + return `${this.attachmentsUrl}/${entity.getId()}/${property}`; } loadFile( @@ -189,7 +158,7 @@ export class CouchdbFileService extends FileService { ): Observable { const path = `${entity.getId()}/${property}`; if (!this.cache[path]) { - this.cache[path] = this.http + this.cache[path] = this.httpClient .get(`${this.attachmentsUrl}/${path}`, { responseType: "blob", }) @@ -211,25 +180,4 @@ export class CouchdbFileService extends FileService { map((url) => this.sanitizer.bypassSecurityTrustUrl(url)), ); } - - private reportProgress( - message: string, - obs: Observable | any>, - ) { - const progress = obs.pipe( - filter( - (e) => - e.type === HttpEventType.DownloadProgress || - e.type === HttpEventType.UploadProgress, - ), - map((e: HttpProgressEvent) => Math.round(100 * (e.loaded / e.total))), - ); - const ref = this.snackbar.openFromComponent(ProgressComponent, { - data: { message, progress }, - }); - progress.subscribe({ - complete: () => ref.dismiss(), - error: () => ref.dismiss(), - }); - } } diff --git a/src/app/features/file/edit-file/edit-file.component.ts b/src/app/features/file/edit-file/edit-file.component.ts index 51f031da59..d8db90266f 100644 --- a/src/app/features/file/edit-file/edit-file.component.ts +++ b/src/app/features/file/edit-file/edit-file.component.ts @@ -24,6 +24,18 @@ import { NotAvailableOfflineError } from "../../../core/session/not-available-of import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens"; import { FileFieldConfig } from "../file.datatype"; +export const EditFileComponent_IMPORTS = [ + MatFormFieldModule, + NgClass, + MatInputModule, + ReactiveFormsModule, + MatTooltipModule, + NgIf, + MatButtonModule, + FontAwesomeModule, + ErrorHintComponent, +]; + /** * This component should be used as a `editComponent` when a property should store files. * It allows to show, upload and remove files. @@ -33,17 +45,7 @@ import { FileFieldConfig } from "../file.datatype"; selector: "app-edit-file", templateUrl: "./edit-file.component.html", styleUrls: ["./edit-file.component.scss"], - imports: [ - MatFormFieldModule, - NgClass, - MatInputModule, - ReactiveFormsModule, - MatTooltipModule, - NgIf, - MatButtonModule, - FontAwesomeModule, - ErrorHintComponent, - ], + imports: EditFileComponent_IMPORTS, standalone: true, }) export class EditFileComponent extends EditComponent implements OnInit { @@ -103,6 +105,12 @@ export class EditFileComponent extends EditComponent implements OnInit { }); } + /** + * Template method to allow easy override of mapping the initialValue from the formControl. + * @protected + */ + protected setInitialValue() {} + async onFileSelected(file: File) { // directly reset input so subsequent selections with the same name also trigger the change event this.fileUploadInput.nativeElement.value = ""; @@ -159,7 +167,9 @@ export class EditFileComponent extends EditComponent implements OnInit { if (this.initialValue && this.formControl.value === this.initialValue) { this.showFile(); } else { - this.fileUploadInput.nativeElement.click(); + if (this.formControl.enabled) { + this.fileUploadInput.nativeElement.click(); + } } } diff --git a/src/app/features/file/file.service.ts b/src/app/features/file/file.service.ts index 0743d11eea..669a6c7c4d 100644 --- a/src/app/features/file/file.service.ts +++ b/src/app/features/file/file.service.ts @@ -2,19 +2,37 @@ import { Entity, EntityConstructor } from "../../core/entity/model/entity"; import { Observable } from "rxjs"; import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; import { EntityRegistry } from "../../core/entity/database-entity.decorator"; -import { filter } from "rxjs/operators"; +import { filter, map, shareReplay } from "rxjs/operators"; import { Logging } from "../../core/logging/logging.service"; import { SafeUrl } from "@angular/platform-browser"; import { FileDatatype } from "./file.datatype"; import { waitForChangeTo } from "../../core/session/session-states/session-utils"; import { SyncState } from "../../core/session/session-states/sync-state.enum"; import { SyncStateSubject } from "../../core/session/session-type"; +import { + HttpClient, + HttpEvent, + HttpEventType, + HttpProgressEvent, + HttpResponse, +} from "@angular/common/http"; +import { ProgressComponent } from "./progress/progress.component"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { inject, Optional } from "@angular/core"; +import { ShowFileComponent } from "./show-file/show-file.component"; +import { MatDialog } from "@angular/material/dialog"; /** * This service allow handles the logic for files/attachments. * Files can be uploaded, shown and removed. */ export abstract class FileService { + protected snackbar: MatSnackBar = inject(MatSnackBar); + protected dialog: MatDialog = inject(MatDialog); + protected httpClient: HttpClient = inject(HttpClient, { + optional: true, + }); + protected constructor( protected entityMapper: EntityMapperService, protected entities: EntityRegistry, @@ -83,8 +101,37 @@ export abstract class FileService { * @param entity * @param property where a file previously has been uploaded */ - abstract showFile(entity: Entity, property: string): void; + showFile(entity: Entity, property: string): void { + const obs = this.httpClient + .get(this.getShowFileUrl(entity, property), { + responseType: "blob", + reportProgress: true, + observe: "events", + headers: { "ngsw-bypass": "" }, + }) + .pipe(shareReplay()); + this.reportProgress($localize`Loading "${entity[property]}"`, obs); + obs + .pipe(filter((e) => e.type === HttpEventType.Response)) + .subscribe((e: HttpResponse) => { + const fileURL = URL.createObjectURL(e.body); + const win = window.open(fileURL, "_blank"); + if (!win || win.closed || typeof win.closed == "undefined") { + // When it takes more than a few (2-5) seconds to open the file, the browser might block the popup + this.dialog.open(ShowFileComponent, { data: fileURL }); + } + }); + } + /** + * Template method to be request a file, so that it can be used in the default showFile implementation + */ + protected abstract getShowFileUrl(entity: Entity, property: string): string; + /** + * Loads the file and returns it as a blob. + * @param entity + * @param property + */ abstract loadFile(entity: Entity, property: string): Observable; /** @@ -98,4 +145,25 @@ export abstract class FileService { entity: Entity, property: string, ): Observable; + + protected reportProgress( + message: string, + obs: Observable | any>, + ) { + const progress = obs.pipe( + filter( + (e) => + e.type === HttpEventType.DownloadProgress || + e.type === HttpEventType.UploadProgress, + ), + map((e: HttpProgressEvent) => Math.round(100 * (e.loaded / e.total))), + ); + const ref = this.snackbar.openFromComponent(ProgressComponent, { + data: { message, progress }, + }); + progress.subscribe({ + complete: () => ref.dismiss(), + error: () => ref.dismiss(), + }); + } } diff --git a/src/app/features/file/mock-file.service.ts b/src/app/features/file/mock-file.service.ts index 0b2da650c3..408255a51c 100644 --- a/src/app/features/file/mock-file.service.ts +++ b/src/app/features/file/mock-file.service.ts @@ -34,11 +34,15 @@ export class MockFileService extends FileService { return EMPTY; } - showFile(entity: Entity, property: string): void { + override showFile(entity: Entity, property: string): void { const url = this.fileMap.get(`${entity.getId()}:${property}`); window.open(url, "_blank"); } + protected override getShowFileUrl(entity: Entity, property: string): string { + return ""; + } + loadFile(entity: Entity, property: string): Observable { const url = this.fileMap.get(`${entity.getId()}:${property}`); return of(this.sanitizer.bypassSecurityTrustUrl(url)); diff --git a/src/app/features/template-export/template-export-api/template-export-api.service.spec.ts b/src/app/features/template-export/template-export-api/template-export-api.service.spec.ts new file mode 100644 index 0000000000..24aa849921 --- /dev/null +++ b/src/app/features/template-export/template-export-api/template-export-api.service.spec.ts @@ -0,0 +1,169 @@ +import { TestBed } from "@angular/core/testing"; + +import { TemplateExportApiService } from "./template-export-api.service"; +import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; +import { EntityRegistry } from "../../../core/entity/database-entity.decorator"; +import { SyncStateSubject } from "../../../core/session/session-type"; +import { lastValueFrom, of } from "rxjs"; +import { SyncState } from "../../../core/session/session-states/sync-state.enum"; +import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens"; +import { provideHttpClientTesting } from "@angular/common/http/testing"; +import { + HttpClient, + HttpHeaders, + HttpResponse, + provideHttpClient, + withInterceptorsFromDi, +} from "@angular/common/http"; +import { TemplateExport } from "../template-export.entity"; +import { + mockEntityMapper, + MockEntityMapperService, +} from "../../../core/entity/entity-mapper/mock-entity-mapper-service"; + +describe("TemplateExportApiService", () => { + let service: TemplateExportApiService; + + let entityMapper: MockEntityMapperService; + + beforeEach(() => { + entityMapper = mockEntityMapper(); + + TestBed.configureTestingModule({ + providers: [ + { provide: EntityMapperService, useValue: entityMapper }, + EntityRegistry, + { + provide: SyncStateSubject, + useValue: of(SyncState.COMPLETED), + }, + { provide: NAVIGATOR_TOKEN, useValue: { onLine: true } }, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], + }); + service = TestBed.inject(TemplateExportApiService); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should skip deletion requests silently", async () => { + const templateEntity = new TemplateExport(); + + const resultOne = await lastValueFrom( + service.removeFile(templateEntity, "templateId"), + ); + expect(resultOne).toBe(true); + + const resultAll = await lastValueFrom( + service.removeAllFiles(templateEntity), + ); + expect(resultAll).toBe(true); + }); + + it("should upload file", async () => { + const entity = new TemplateExport(); + const mockFile = new File([""], "filename"); + + const mockPOST = spyOn(TestBed.inject(HttpClient), "post").and.returnValue( + of({ templateId: "TEST_ID" }), + ); + + const result = await lastValueFrom( + service.uploadFile(mockFile, entity, "templateId"), + ); + + expect(result).toBe("TEST_ID"); + expect(mockPOST).toHaveBeenCalledWith( + service.BACKEND_URL + "template", + jasmine.any(FormData), + ); + + const finalEntity = entityMapper.get( + entity.getType(), + entity.getId(), + ) as TemplateExport; + expect(finalEntity.templateId).toBe("TEST_ID"); + }); + + it("should throw error trying to upload while offline", async () => { + const entity = new TemplateExport(); + const mockFile = new File([""], "filename"); + + // @ts-ignore + TestBed.inject(NAVIGATOR_TOKEN).onLine = false; + const mockPOST = spyOn(TestBed.inject(HttpClient), "post"); + + await expectAsync( + lastValueFrom(service.uploadFile(mockFile, entity, "templateId")), + ).toBeRejectedWithError(); + expect(mockPOST).not.toHaveBeenCalled(); + }); + + it("should request a generated file from API and use Content-Disposition", async () => { + const templateEntity = new TemplateExport("test-template-id"); + const dataEntity = { name: "abc" }; + + const mockResponse = new HttpResponse({ + body: new ArrayBuffer(10), + headers: new HttpHeaders({ + "Content-Disposition": 'filename="cert_John%20Doe.pdf"', + }), + status: 200, + }); + const mockApiResponse = spyOn( + TestBed.inject(HttpClient), + "post", + ).and.returnValue(of(mockResponse)); + + const result = await lastValueFrom( + service.generatePdfFromTemplate(templateEntity.getId(), dataEntity), + ); + + expect(result).toEqual({ + filename: "cert_John Doe.pdf", + file: mockResponse.body, + }); + expect(mockApiResponse).toHaveBeenCalledWith( + service.BACKEND_URL + "render/" + templateEntity.getId(), + { + convertTo: "pdf", + data: dataEntity, + }, + jasmine.any(Object), + ); + }); + + it("should request a generated file from API with default fileName", async () => { + const templateEntity = new TemplateExport("test-template-id"); + const dataEntity = { name: "abc" }; + + const mockResponse = new HttpResponse({ + body: new ArrayBuffer(10), + status: 200, + }); + const mockApiResponse = spyOn( + TestBed.inject(HttpClient), + "post", + ).and.returnValue(of(mockResponse)); + + const result = await lastValueFrom( + service.generatePdfFromTemplate(templateEntity.getId(), dataEntity), + ); + + expect(result).toEqual({ + filename: "TemplateExport_test-template-id", + file: mockResponse.body, + }); + expect(mockApiResponse).toHaveBeenCalledWith( + service.BACKEND_URL + "render/" + templateEntity.getId(), + { + convertTo: "pdf", + data: dataEntity, + }, + jasmine.any(Object), + ); + }); +}); diff --git a/src/app/features/template-export/template-export-api/template-export-api.service.ts b/src/app/features/template-export/template-export-api/template-export-api.service.ts new file mode 100644 index 0000000000..a21e777901 --- /dev/null +++ b/src/app/features/template-export/template-export-api/template-export-api.service.ts @@ -0,0 +1,160 @@ +import { Inject, Injectable } from "@angular/core"; +import { FileService } from "../../file/file.service"; +import { SafeUrl } from "@angular/platform-browser"; +import { Entity } from "app/core/entity/model/entity"; +import { Observable, of, throwError } from "rxjs"; +import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; +import { EntityRegistry } from "../../../core/entity/database-entity.decorator"; +import { SyncStateSubject } from "../../../core/session/session-type"; +import { HttpResponse } from "@angular/common/http"; +import { NotAvailableOfflineError } from "../../../core/session/not-available-offline.error"; +import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens"; +import { switchMap } from "rxjs/operators"; +import { TemplateExport } from "../template-export.entity"; +import { Logging } from "../../../core/logging/logging.service"; + +/** + * Format of API response body upon uploading a new template file. + */ +interface TemplateUploadResponseDto { + templateId: string; +} + +/** + * Format of API request body to render a PDF from a template. + * TemplateId is provided via URL path. + */ +interface TemplateRenderRequestDto { + /** + * target file type (e.g. "pdf") + */ + convertTo: string; + + /** + * The data used to fill placeholders in the template. + */ + data: Object; +} + +export interface TemplateExportResult { + filename: string; + file: ArrayBuffer; +} + +/** + * Interact with the PDF Template Generation API that uses File Templates to generate custom pdf documents. + */ +@Injectable({ + providedIn: "root", +}) +export class TemplateExportApiService extends FileService { + readonly BACKEND_URL = "/query/api/v1/export/"; + + constructor( + entityMapper: EntityMapperService, + entities: EntityRegistry, + syncState: SyncStateSubject, + @Inject(NAVIGATOR_TOKEN) private navigator: Navigator, + ) { + super(entityMapper, entities, syncState); + } + + /* + --- FileService methods --- + */ + + /** + * Upload a new template file to the API. + * @param file to be uploaded to the API as a template + * @param entity + * @param property + * @return The template ID generated by the API + */ + uploadFile( + file: File, + entity: TemplateExport, + property: string, + ): Observable { + if (!this.navigator.onLine) { + return throwError(() => new NotAvailableOfflineError("File Attachments")); + } + + const formData = new FormData(); + formData.append("template", file, file.name); + + return this.httpClient.post(this.BACKEND_URL + "template", formData).pipe( + switchMap(async (res: TemplateUploadResponseDto) => { + entity.templateId = res.templateId; + await this.entityMapper.save(entity); + return res.templateId; + }), + ); + } + + protected override getShowFileUrl( + entity: TemplateExport, + property: string, + ): string { + return this.BACKEND_URL + "template/" + entity.getId(); + } + + loadFile(entity: Entity, property: string): Observable { + // should not be required for our use cases of the Template Export API + throw new Error("Method not implemented."); + } + + removeFile(entity: Entity, property: string): Observable { + // we do not do explicit file removal due to the design of the API + Logging.debug("skipping file removal for Template Export API"); + return of(true); + } + removeAllFiles(entity: Entity): Observable { + Logging.debug("skipping file removal for Template Export API"); + return of(true); + } + + /* + --- PDF Generation API methods --- + */ + + /** + * Generate a PDF applying actual data to an existing template. + * @param templateEntityId The id of the TemplateExport entity (not the template ID of the PDF API) + * @param data The data object (typically an entity) to be applied to the template + * @return An array buffer of the generated PDF + */ + generatePdfFromTemplate( + templateEntityId: string, + data: Object, + ): Observable { + return this.httpClient + .post( + this.BACKEND_URL + "render/" + templateEntityId, + { + convertTo: "pdf", + data: data, + } as TemplateRenderRequestDto, + { observe: "response", responseType: "arraybuffer" }, + ) + .pipe( + switchMap(async (res: HttpResponse) => { + // the API returns the filename in the Content-Disposition header as a URL-encoded string with special delimiters + const filenameMatch = decodeURIComponent( + res.headers.get("Content-Disposition"), + ).match(/filename="(.+)"/); + + let fileName: string; + if (filenameMatch && filenameMatch.length > 1) { + fileName = filenameMatch[1]; + } else { + fileName = templateEntityId.replace(":", "_"); + } + + return { + filename: fileName, + file: res.body, + }; + }), + ); + } +} diff --git a/src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.spec.ts b/src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.spec.ts new file mode 100644 index 0000000000..963d35359a --- /dev/null +++ b/src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { EditTemplateExportFileComponent } from "./edit-template-export-file.component"; +import { AlertService } from "../../../core/alerts/alert.service"; +import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; +import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens"; +import { TemplateExportApiService } from "../template-export-api/template-export-api.service"; + +describe("EditTemplateExportFileComponent", () => { + let component: EditTemplateExportFileComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [EditTemplateExportFileComponent], + providers: [ + { provide: AlertService, useValue: null }, + { provide: EntityMapperService, useValue: null }, + { provide: NAVIGATOR_TOKEN, useValue: { onLine: true } }, + { provide: TemplateExportApiService, useValue: null }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(EditTemplateExportFileComponent); + component = fixture.componentInstance; + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.ts b/src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.ts new file mode 100644 index 0000000000..fae3522c82 --- /dev/null +++ b/src/app/features/template-export/template-export-file-datatype/edit-template-export-file.component.ts @@ -0,0 +1,32 @@ +import { Component, Inject } from "@angular/core"; +import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator"; +import { + EditFileComponent, + EditFileComponent_IMPORTS, +} from "../../file/edit-file/edit-file.component"; +import { AlertService } from "../../../core/alerts/alert.service"; +import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; +import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens"; +import { TemplateExportApiService } from "../template-export-api/template-export-api.service"; + +/** + * An edit component that allows to manage template files stored in the PDF Generator API. + */ +@DynamicComponent("EditTemplateExportFile") +@Component({ + selector: "app-template-export-file", + templateUrl: "../../file/edit-file/edit-file.component.html", + styleUrls: ["../../file/edit-file/edit-file.component.scss"], + imports: EditFileComponent_IMPORTS, + standalone: true, +}) +export class EditTemplateExportFileComponent extends EditFileComponent { + constructor( + templateExportApi: TemplateExportApiService, + alertService: AlertService, + entityMapper: EntityMapperService, + @Inject(NAVIGATOR_TOKEN) navigator: Navigator, + ) { + super(templateExportApi, alertService, entityMapper, navigator); + } +} diff --git a/src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.spec.ts b/src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.spec.ts new file mode 100644 index 0000000000..57cf463a3d --- /dev/null +++ b/src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.spec.ts @@ -0,0 +1,6 @@ +import { testDatatype } from "../../../core/entity/schema/entity-schema.service.spec"; +import { TemplateExportFileDatatype } from "./template-export-file.datatype"; + +describe("Schema data type: template-export-file", () => { + testDatatype(new TemplateExportFileDatatype(), "123", "123"); +}); diff --git a/src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.ts b/src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.ts new file mode 100644 index 0000000000..6b900987f2 --- /dev/null +++ b/src/app/features/template-export/template-export-file-datatype/template-export-file.datatype.ts @@ -0,0 +1,16 @@ +import { Injectable } from "@angular/core"; +import { StringDatatype } from "../../../core/basic-datatypes/string/string.datatype"; + +/** + * Datatype for managing template files of the PDF Generator API. + * + * Only a string templateId of the API is stored on the entity itself. + */ +@Injectable() +export class TemplateExportFileDatatype extends StringDatatype { + static override dataType = "template-export-file"; + static override label: string = undefined; // only used internally, not offered to users in Admin UI + + override viewComponent = "ViewFile"; + override editComponent = "EditTemplateExportFile"; +} diff --git a/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html new file mode 100644 index 0000000000..21ce8cc1e6 --- /dev/null +++ b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html @@ -0,0 +1,45 @@ + +

Generate File from Template

+

+ Select the template based on which to generate a file for the current record + or + configure available templates. +

+ + +
+ + + + + + diff --git a/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.scss b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.scss new file mode 100644 index 0000000000..02d0fde6db --- /dev/null +++ b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.scss @@ -0,0 +1,12 @@ +mat-dialog-content { + max-width: 1024px; +} + +app-entity-select { + width: 100%; + display: inline-block; + + ::ng-deep mat-form-field { + width: 100%; + } +} diff --git a/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.spec.ts b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.spec.ts new file mode 100644 index 0000000000..05fc6ca5b7 --- /dev/null +++ b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.spec.ts @@ -0,0 +1,146 @@ +import { + ComponentFixture, + fakeAsync, + TestBed, + tick, +} from "@angular/core/testing"; + +import { TemplateExportSelectionDialogComponent } from "./template-export-selection-dialog.component"; +import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; +import { Entity } from "../../../core/entity/model/entity"; +import { + TemplateExportApiService, + TemplateExportResult, +} from "../template-export-api/template-export-api.service"; +import { DownloadService } from "../../../core/export/download-service/download.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { EntityAbility } from "../../../core/permissions/ability/entity-ability"; +import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; +import { mockEntityMapper } from "../../../core/entity/entity-mapper/mock-entity-mapper-service"; +import { EntityRegistry } from "../../../core/entity/database-entity.decorator"; +import { ActivatedRoute } from "@angular/router"; +import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; +import { NoopAnimationsModule } from "@angular/platform-browser/animations"; +import { delay, of } from "rxjs"; +import { map } from "rxjs/operators"; +import { AlertService } from "../../../core/alerts/alert.service"; +import { TemplateExport } from "../template-export.entity"; + +describe("TemplateExportSelectionDialogComponent", () => { + let component: TemplateExportSelectionDialogComponent; + let fixture: ComponentFixture; + + let mockDialogRef: jasmine.SpyObj< + MatDialogRef + >; + let mockPdfGeneratorApiService: jasmine.SpyObj; + let mockDownloadService: jasmine.SpyObj; + + let testEntity: Entity; + + beforeEach(async () => { + testEntity = new TestEntity(); + + mockPdfGeneratorApiService = jasmine.createSpyObj([ + "generatePdfFromTemplate", + ]); + mockDownloadService = jasmine.createSpyObj(["triggerDownload"]); + mockDialogRef = jasmine.createSpyObj(["close"]); + const mockAbility = jasmine.createSpyObj(["cannot", "on"]); + mockAbility.on.and.returnValue(() => null); + + await TestBed.configureTestingModule({ + imports: [ + TemplateExportSelectionDialogComponent, + FontAwesomeTestingModule, + NoopAnimationsModule, + ], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: testEntity }, + { provide: MatDialogRef, useValue: mockDialogRef }, + { + provide: TemplateExportApiService, + useValue: mockPdfGeneratorApiService, + }, + { provide: DownloadService, useValue: mockDownloadService }, + // required by child components: + { provide: EntityAbility, useValue: mockAbility }, + { provide: EntityMapperService, useValue: mockEntityMapper() }, + EntityRegistry, + { provide: ActivatedRoute, useValue: null }, + { + provide: AlertService, + useValue: jasmine.createSpyObj(["addWarning"]), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(TemplateExportSelectionDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should only show applicable templates for the entity type", () => { + const template1 = new TemplateExport(); + template1.applicableForEntityTypes = [TestEntity.ENTITY_TYPE]; + expect(component.templateEntityFilter(template1)).toBeTrue(); + + const template2 = new TemplateExport(); + template2.applicableForEntityTypes = ["other type", TestEntity.ENTITY_TYPE]; + expect(component.templateEntityFilter(template2)).toBeTrue(); + + const template3 = new TemplateExport(); + template3.applicableForEntityTypes = ["other type"]; + expect(component.templateEntityFilter(template3)).toBeFalse(); + }); + + it("should trigger download with API response when requesting file", fakeAsync(() => { + const entity = new TestEntity(); + entity.name = "test entity"; + component.entity = entity; + + const mockResponse: TemplateExportResult = { + filename: "test.pdf", + file: new ArrayBuffer(10), + }; + mockPdfGeneratorApiService.generatePdfFromTemplate.and.returnValue( + of(mockResponse).pipe(delay(100)), + ); + + component.requestFile(); + tick(1); + expect(component.loadingRequestedFile).toBeTrue(); + tick(100); + expect(component.loadingRequestedFile).toBeFalse(); + + expect( + mockPdfGeneratorApiService.generatePdfFromTemplate, + ).toHaveBeenCalled(); + expect(mockDownloadService.triggerDownload).toHaveBeenCalledWith( + mockResponse.file, + "pdf", + mockResponse.filename, + ); + expect(mockDialogRef.close).toHaveBeenCalled(); + })); + + it("should disable loading but not close dialog if API request fails", fakeAsync(() => { + mockPdfGeneratorApiService.generatePdfFromTemplate.and.returnValue( + of(false).pipe( + delay(100), + map(() => { + throw new Error(); + }), + ), + ); + + component.requestFile(); + tick(1); + expect(component.loadingRequestedFile).toBeTrue(); + tick(100); + expect(component.loadingRequestedFile).toBeFalse(); + + expect(mockDownloadService.triggerDownload).not.toHaveBeenCalled(); + expect(mockDialogRef.close).not.toHaveBeenCalled(); + })); +}); diff --git a/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts new file mode 100644 index 0000000000..5c43c2ec6a --- /dev/null +++ b/src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts @@ -0,0 +1,91 @@ +import { Component, Inject, Input } from "@angular/core"; +import { + MAT_DIALOG_DATA, + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogRef, +} from "@angular/material/dialog"; +import { Entity } from "../../../core/entity/model/entity"; +import { + TemplateExportApiService, + TemplateExportResult, +} from "../template-export-api/template-export-api.service"; +import { MatButton } from "@angular/material/button"; +import { DownloadService } from "../../../core/export/download-service/download.service"; +import { TemplateExport } from "../template-export.entity"; +import { EntitySelectComponent } from "../../../core/common-components/entity-select/entity-select.component"; +import { FormControl } from "@angular/forms"; +import { RouterLink } from "@angular/router"; +import { DisableEntityOperationDirective } from "../../../core/permissions/permission-directive/disable-entity-operation.directive"; +import { MatProgressSpinner } from "@angular/material/progress-spinner"; +import { MatProgressBar } from "@angular/material/progress-bar"; +import { AlertService } from "../../../core/alerts/alert.service"; + +/** + * Popup for user to select one of the available templates + * and manage the PDF/file generation process for a specific entity. + */ +@Component({ + selector: "app-file-template-selection-dialog-component", + standalone: true, + imports: [ + MatDialogContent, + MatDialogActions, + MatButton, + EntitySelectComponent, + MatDialogClose, + RouterLink, + DisableEntityOperationDirective, + MatProgressSpinner, + MatProgressBar, + ], + templateUrl: "./template-export-selection-dialog.component.html", + styleUrl: "./template-export-selection-dialog.component.scss", +}) +export class TemplateExportSelectionDialogComponent { + @Input() entity: Entity; + + templateSelectionForm: FormControl = new FormControl(); + + TemplateExport = TemplateExport; + templateEntityFilter: (e: TemplateExport) => boolean = (e) => + e.applicableForEntityTypes.includes(this.entity.getType()); + + loadingRequestedFile: boolean; + + constructor( + @Inject(MAT_DIALOG_DATA) data: Entity, + private dialogRef: MatDialogRef, + private templateExportApi: TemplateExportApiService, + private downloadService: DownloadService, + private alertService: AlertService, + ) { + this.entity = data; + } + + requestFile() { + const templateId = this.templateSelectionForm.value; + + this.loadingRequestedFile = true; + this.templateExportApi + .generatePdfFromTemplate(templateId, this.entity) + .subscribe({ + error: (error) => { + this.alertService.addWarning( + $localize`Failed to generate document [${error}]`, + ); + this.loadingRequestedFile = false; + }, + next: (templateResult: TemplateExportResult) => { + this.downloadService.triggerDownload( + templateResult.file, + "pdf", + templateResult.filename ?? this.entity?.toString(), + ); + this.loadingRequestedFile = false; + this.dialogRef.close(true); + }, + }); + } +} diff --git a/src/app/features/template-export/template-export-service/template-export.service.spec.ts b/src/app/features/template-export/template-export-service/template-export.service.spec.ts new file mode 100644 index 0000000000..14c2c564d0 --- /dev/null +++ b/src/app/features/template-export/template-export-service/template-export.service.spec.ts @@ -0,0 +1,34 @@ +import { TestBed } from "@angular/core/testing"; + +import { TemplateExportService } from "./template-export.service"; +import { MatDialog } from "@angular/material/dialog"; +import { TemplateExportSelectionDialogComponent } from "../template-export-selection-dialog/template-export-selection-dialog.component"; + +describe("TemplateExportService", () => { + let service: TemplateExportService; + let mockDialog: jasmine.SpyObj; + + beforeEach(() => { + mockDialog = jasmine.createSpyObj("MatDialog", ["open"]); + + TestBed.configureTestingModule({ + providers: [{ provide: MatDialog, useValue: mockDialog }], + }); + service = TestBed.inject(TemplateExportService); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should open dialog with given entity", async () => { + const data = {}; + const result = await service.generateFile(data); + + expect(mockDialog.open).toHaveBeenCalledWith( + TemplateExportSelectionDialogComponent, + jasmine.objectContaining({ data: data }), + ); + expect(result).toBeTrue(); + }); +}); diff --git a/src/app/features/template-export/template-export-service/template-export.service.ts b/src/app/features/template-export/template-export-service/template-export.service.ts new file mode 100644 index 0000000000..598702409e --- /dev/null +++ b/src/app/features/template-export/template-export-service/template-export.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from "@angular/core"; +import { Entity } from "../../../core/entity/model/entity"; +import { MatDialog } from "@angular/material/dialog"; +import { TemplateExportSelectionDialogComponent } from "../template-export-selection-dialog/template-export-selection-dialog.component"; + +/** + * Triggers a user flow to + * generate files based on a file template from an entity. + */ +@Injectable({ + providedIn: "root", +}) +export class TemplateExportService { + constructor(private dialog: MatDialog) {} + + /** + * Open a dialog for the user to select a template and generate a file from it for the given entity. + * @param entity The entity or other data object to provide placeholder values for the template + * @return Boolean whether the action was successfully completed + */ + async generateFile(entity: Entity | Object): Promise { + this.dialog.open(TemplateExportSelectionDialogComponent, { data: entity }); + + return true; + } +} diff --git a/src/app/features/template-export/template-export.entity.ts b/src/app/features/template-export/template-export.entity.ts new file mode 100644 index 0000000000..985b7388c6 --- /dev/null +++ b/src/app/features/template-export/template-export.entity.ts @@ -0,0 +1,88 @@ +import { Entity } from "../../core/entity/model/entity"; +import { DatabaseEntity } from "../../core/entity/database-entity.decorator"; +import { DatabaseField } from "../../core/entity/database-field.decorator"; +import { LongTextDatatype } from "../../core/basic-datatypes/string/long-text.datatype"; +import { FileFieldConfig } from "../file/file.datatype"; +import { EntityBlockConfig } from "../../core/basic-datatypes/entity/entity-block/entity-block-config"; +import { TemplateExportFileDatatype } from "./template-export-file-datatype/template-export-file.datatype"; + +/** + * Represents a TemplateExport that can be used to generate PDFs via API, + * replacing placeholders in the file with data from an entity. + * + * A TemplateExport entity represents the meta-data of a template and can be added and manage by admin users. + */ +@DatabaseEntity("TemplateExport") +export class TemplateExport extends Entity { + static override label = $localize`:TemplateExport:Export Template`; + static override labelPlural = $localize`:TemplateExport:Export Templates`; + static override toStringAttributes = ["title"]; + static override toBlockDetailsAttributes: EntityBlockConfig = { + title: "title", + fields: ["description"], + }; + static override route = "admin/template-export"; + + /** + * human-readable label + */ + @DatabaseField({ + label: $localize`:TemplateExport:Title`, + validators: { required: true }, + }) + title: string; + + /** + * Additional description to guide users + */ + @DatabaseField({ + label: $localize`:TemplateExport:Description`, + dataType: LongTextDatatype.dataType, + }) + description: string; + + /** + * The entity type(s) this template can be used with (i.e. placeholders matching the entity type) + */ + @DatabaseField({ + label: $localize`:TemplateExport:Applicable Entity Types`, + labelShort: $localize`:TemplateExport:Entity Types`, + editComponent: "EditEntityTypeDropdown", + isArray: true, + }) + applicableForEntityTypes: string[]; + + /** + * File (storing the file name) of the template uploaded to the server. + */ + @DatabaseField({ + label: $localize`:TemplateExport:Template File`, + description: $localize`:TemplateExport:Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF.`, + validators: { required: true }, + dataType: TemplateExportFileDatatype.dataType, + additional: { + acceptedFileTypes: + ".docx, .doc, .odt, .xlsx, .xls, .ods, .pptx, .ppt, .odp", + } as FileFieldConfig, + }) + templateFile: string; + + /** + * ID of the template file in the server-side managed API. + * + * This is not displayed to users - they interact with the templateFile property instead + * while this templateId is automatically set when the file is uploaded. + */ + @DatabaseField() + templateId: string; + + /** + * A string with the pattern including placeholders for the file name of the generated files. + */ + @DatabaseField({ + label: $localize`:TemplateExport:File name pattern for generated file`, + labelShort: $localize`:TemplateExport:File name pattern`, + description: $localize`:TemplateExport:The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf").`, + }) + targetFileName: string; +} diff --git a/src/app/features/template-export/template-export.module.ts b/src/app/features/template-export/template-export.module.ts new file mode 100644 index 0000000000..29389d7a64 --- /dev/null +++ b/src/app/features/template-export/template-export.module.ts @@ -0,0 +1,130 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { AsyncComponent, ComponentRegistry } from "../../dynamic-components"; +import { RouterService } from "../../core/config/dynamic-routing/router.service"; +import { ViewConfig } from "../../core/config/dynamic-routing/view-config.interface"; +import { EntityDetailsConfig } from "../../core/entity-details/EntityDetailsConfig"; +import { EntityListConfig } from "../../core/entity-list/EntityListConfig"; +import { AdminOverviewService } from "../../core/admin/admin-overview/admin-overview.service"; +import { EntityActionsMenuService } from "../../core/entity-details/entity-actions-menu/entity-actions-menu.service"; +import { DefaultDatatype } from "../../core/entity/default-datatype/default.datatype"; +import { Entity } from "../../core/entity/model/entity"; +import { TemplateExportFileDatatype } from "./template-export-file-datatype/template-export-file.datatype"; +import { TemplateExport } from "./template-export.entity"; +import { TemplateExportService } from "./template-export-service/template-export.service"; + +/** + * Manage template files with placeholders that can be used to render files for export of entities. + */ +@NgModule({ + declarations: [], + imports: [CommonModule], + providers: [ + { + provide: DefaultDatatype, + useClass: TemplateExportFileDatatype, + multi: true, + }, + ], +}) +export class TemplateExportModule { + static databaseEntities = [TemplateExport]; + + constructor( + components: ComponentRegistry, + routerService: RouterService, + adminOverviewService: AdminOverviewService, + entityActionsMenuService: EntityActionsMenuService, + templateExportService: TemplateExportService, + ) { + components.addAll(dynamicComponents); + routerService.addRoutes(viewConfigs); + + entityActionsMenuService.registerActions([ + { + action: "template-export", + label: $localize`:entity context menu:Generate File`, + icon: "print", + tooltip: $localize`:entity context menu tooltip:Create a file based on a selected template.`, + permission: "read", + execute: async (e: Entity) => templateExportService.generateFile(e), + }, + ]); + + adminOverviewService.menuItems.push({ + label: $localize`:admin menu item:Manage Export Templates`, + link: TemplateExport.route, + }); + } +} + +const dynamicComponents: [string, AsyncComponent][] = [ + [ + "EditTemplateExportFile", + () => + import( + "./template-export-file-datatype/edit-template-export-file.component" + ).then((c) => c.EditTemplateExportFileComponent), + ], +]; + +const viewConfigs: ViewConfig[] = [ + // List View + { + _id: "view:" + TemplateExport.route, + component: "EntityList", + config: { + entityType: TemplateExport.ENTITY_TYPE, + columns: ["title", "description", "applicableForEntityTypes"], + filters: [{ id: "applicableForEntityTypes" }], + } as EntityListConfig, + }, + + // Details View + { + _id: "view:" + TemplateExport.route + "/:id", + component: "EntityDetails", + config: { + entityType: TemplateExport.ENTITY_TYPE, + panels: [ + { + components: [ + { + component: "Form", + config: { + fieldGroups: [ + { + fields: [ + "title", + "description", + "applicableForEntityTypes", + ], + }, + { + fields: [ + { + id: "template_explanation", + editComponent: "EditDescriptionOnly", + label: $localize`:TemplateExport:Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF.`, + }, + "templateFile", + "targetFileName", + ], + }, + ], + }, + }, + ], + }, + ], + } as EntityDetailsConfig, + }, +]; diff --git a/src/assets/locale/messages.de.xlf b/src/assets/locale/messages.de.xlf index f2c41e4885..ad0b213e9e 100644 --- a/src/assets/locale/messages.de.xlf +++ b/src/assets/locale/messages.de.xlf @@ -531,6 +531,10 @@ src/app/core/config/config-fix.ts 843 + + src/app/features/template-export/template-export.entity.ts + 39 + src/app/features/todos/model/todo-default-configs.ts 19 @@ -1718,7 +1722,7 @@ File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 130 + 138 @@ -1727,7 +1731,7 @@ File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 132 + 140 @@ -1736,7 +1740,7 @@ File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 135 + 143 @@ -1745,7 +1749,7 @@ Message for user src/app/features/file/edit-file/edit-file.component.ts - 185 + 195 @@ -4461,6 +4465,10 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi src/app/core/config/config-fix.ts 856 + + src/app/features/template-export/template-export.entity.ts + 30 + Type @@ -6662,6 +6670,10 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi src/app/features/location/map-popup/map-popup.component.html 27 + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 44 + Configure Field "" @@ -8164,6 +8176,198 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Button to download data + + Generate File from Template + Datei aus Template generieren + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 2 + + + + Select the template based on which to generate a file for the current record or configure available templates. + Wählen Sie ein Template aus dem eine Datei für den aktuellen Datensatz generiert werden soll oder konfigurieren Sie verfügbare Templates. + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 3,15 + + + + Template + Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 21 + + + + Select a file template + Wählen Sie ein Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 23 + + + + Generate File + Datei generieren + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 37 + + + src/app/features/template-export/template-export.module.ts + 46 + + + + Failed to generate document [] + Dokument konnte nicht erstellt werden [] + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts + 76 + + + + Export Template + Export Template + + src/app/features/template-export/template-export.entity.ts + 17 + + TemplateExport + + + Export Templates + Export Templates + + src/app/features/template-export/template-export.entity.ts + 18 + + TemplateExport + + + Applicable Entity Types + Verfügbar für folgende Datensatz-Typen + + src/app/features/template-export/template-export.entity.ts + 48 + + TemplateExport + + + Entity Types + Datensatz-Typen + + src/app/features/template-export/template-export.entity.ts + 49 + + TemplateExport + + + Template File + Template-Datei + + src/app/features/template-export/template-export.entity.ts + 59 + + TemplateExport + + + Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF. + Laden Sie ein speziell aufbereitetes Dokument hoch, das Platzhalter enthält. Diese werden durch tatsächlichen Daten des jeweiligen Datensatzes ersetzt, wenn eine Datei generiert wird. + + src/app/features/template-export/template-export.entity.ts + 60 + + TemplateExport + + + File name pattern for generated file + Dateiname der generierten Dateien + + src/app/features/template-export/template-export.entity.ts + 83 + + TemplateExport + + + File name pattern + Dateinamen-Vorlage + + src/app/features/template-export/template-export.entity.ts + 84 + + TemplateExport + + + The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf"). + Der Dateiname beim Download aus diesem Template generierter Dokumente. Sie können hier ebenfalls Platzhalter wie in der Template-Datei benutzen (z.B. "mein-bericht_{d.name}"). + + src/app/features/template-export/template-export.entity.ts + 85 + + TemplateExport + + + Create a file based on a selected template. + Dokument aus einem Template erstellen. + + src/app/features/template-export/template-export.module.ts + 48 + + entity context menu tooltip + + + Manage Export Templates + Export-Templates verwalten + + src/app/features/template-export/template-export.module.ts + 55 + + admin menu item + + + Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF. + Laden Sie hier eine speziell vorbereitete Vorlagendatei hoch. +Die Datei kann Platzhalter enthalten, die durch tatsächliche Daten ersetzt werden, wenn eine Datei für einen ausgewählten Datensatz generiert wird. +Beispielsweise wird {d.name} durch den Wert im Feld „Name“ des jeweiligen Datensatzes ersetzt. +Weitere Informationen finden Sie in der Dokumentation des [Carbone-Systems](https://carbone.io/documentation.html#substitutions). + +Die Platzhalter müssen mit der „Feld ID“ des Feldes in der Datenstruktur des Datensatz-Typen in Aam Digital übereinstimmen. +Sie finden dies im Formular-Editor der Admin-Benutzeroberfläche (Datenstruktur bearbeiten -> Detail-Ansicht) wenn Sie ein bestimmtes Feld bearbeiten, um dessen Details anzuzeigen. + +Template-Dateien können in den meisten Office-Dokumentformaten (odt, docx, ods, xlsx, odp, pptx) oder im PDF-Format hochgeladen werden. + + src/app/features/template-export/template-export.module.ts + 108,116 + + TemplateExport + get signed agreement Vereinbarung unterschreiben diff --git a/src/assets/locale/messages.fr.xlf b/src/assets/locale/messages.fr.xlf index c8954edac8..a6cc6a1483 100644 --- a/src/assets/locale/messages.fr.xlf +++ b/src/assets/locale/messages.fr.xlf @@ -902,6 +902,10 @@ src/app/core/config/config-fix.ts 856 + + src/app/features/template-export/template-export.entity.ts + 30 + Type @@ -1728,6 +1732,10 @@ src/app/core/config/config-fix.ts 843 + + src/app/features/template-export/template-export.entity.ts + 39 + src/app/features/todos/model/todo-default-configs.ts 19 @@ -5241,6 +5249,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/features/location/map-popup/map-popup.component.html 27 + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 44 + Configure Field "" @@ -7750,7 +7762,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 130 + 138 @@ -7759,7 +7771,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 132 + 140 @@ -7768,7 +7780,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 135 + 143 @@ -7777,7 +7789,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Message for user src/app/features/file/edit-file/edit-file.component.ts - 185 + 195 @@ -8228,6 +8240,198 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Button to download data + + Generate File from Template + Generate File from Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 2 + + + + Select the template based on which to generate a file for the current record or configure available templates. + Select the template based on which to generate a file for the current record or configure available templates. + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 3,15 + + + + Template + Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 21 + + + + Select a file template + Select a file template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 23 + + + + Generate File + Generate File + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 37 + + + src/app/features/template-export/template-export.module.ts + 46 + + + + Failed to generate document [] + Failed to generate document [] + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts + 76 + + + + Export Template + Export Template + + src/app/features/template-export/template-export.entity.ts + 17 + + TemplateExport + + + Export Templates + Export Templates + + src/app/features/template-export/template-export.entity.ts + 18 + + TemplateExport + + + Applicable Entity Types + Applicable Entity Types + + src/app/features/template-export/template-export.entity.ts + 48 + + TemplateExport + + + Entity Types + Entity Types + + src/app/features/template-export/template-export.entity.ts + 49 + + TemplateExport + + + Template File + Template File + + src/app/features/template-export/template-export.entity.ts + 59 + + TemplateExport + + + Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF. + Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF. + + src/app/features/template-export/template-export.entity.ts + 60 + + TemplateExport + + + File name pattern for generated file + File name pattern for generated file + + src/app/features/template-export/template-export.entity.ts + 83 + + TemplateExport + + + File name pattern + File name pattern + + src/app/features/template-export/template-export.entity.ts + 84 + + TemplateExport + + + The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf"). + The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf"). + + src/app/features/template-export/template-export.entity.ts + 85 + + TemplateExport + + + Create a file based on a selected template. + Create a file based on a selected template. + + src/app/features/template-export/template-export.module.ts + 48 + + entity context menu tooltip + + + Manage Export Templates + Manage Export Templates + + src/app/features/template-export/template-export.module.ts + 55 + + admin menu item + + + Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF. + Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF. + + src/app/features/template-export/template-export.module.ts + 108,116 + + TemplateExport + get signed agreement get signed agreement diff --git a/src/assets/locale/messages.it.xlf b/src/assets/locale/messages.it.xlf index 7206830b4e..37e059d406 100644 --- a/src/assets/locale/messages.it.xlf +++ b/src/assets/locale/messages.it.xlf @@ -698,6 +698,10 @@ src/app/core/config/config-fix.ts 856 + + src/app/features/template-export/template-export.entity.ts + 30 + Type @@ -1146,6 +1150,10 @@ src/app/core/config/config-fix.ts 843 + + src/app/features/template-export/template-export.entity.ts + 39 + src/app/features/todos/model/todo-default-configs.ts 19 @@ -4873,6 +4881,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/features/location/map-popup/map-popup.component.html 27 + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 44 + Configure Field "" @@ -7443,7 +7455,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 130 + 138 @@ -7452,7 +7464,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 132 + 140 @@ -7461,7 +7473,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File Upload Error Message src/app/features/file/edit-file/edit-file.component.ts - 135 + 143 @@ -7470,7 +7482,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Message for user src/app/features/file/edit-file/edit-file.component.ts - 185 + 195 @@ -8325,6 +8337,198 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Button to download data + + Generate File from Template + Generate File from Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 2 + + + + Select the template based on which to generate a file for the current record or configure available templates. + Select the template based on which to generate a file for the current record or configure available templates. + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 3,15 + + + + Template + Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 21 + + + + Select a file template + Select a file template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 23 + + + + Generate File + Generate File + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 37 + + + src/app/features/template-export/template-export.module.ts + 46 + + + + Failed to generate document [] + Failed to generate document [] + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts + 76 + + + + Export Template + Export Template + + src/app/features/template-export/template-export.entity.ts + 17 + + TemplateExport + + + Export Templates + Export Templates + + src/app/features/template-export/template-export.entity.ts + 18 + + TemplateExport + + + Applicable Entity Types + Applicable Entity Types + + src/app/features/template-export/template-export.entity.ts + 48 + + TemplateExport + + + Entity Types + Entity Types + + src/app/features/template-export/template-export.entity.ts + 49 + + TemplateExport + + + Template File + Template File + + src/app/features/template-export/template-export.entity.ts + 59 + + TemplateExport + + + Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF. + Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF. + + src/app/features/template-export/template-export.entity.ts + 60 + + TemplateExport + + + File name pattern for generated file + File name pattern for generated file + + src/app/features/template-export/template-export.entity.ts + 83 + + TemplateExport + + + File name pattern + File name pattern + + src/app/features/template-export/template-export.entity.ts + 84 + + TemplateExport + + + The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf"). + The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf"). + + src/app/features/template-export/template-export.entity.ts + 85 + + TemplateExport + + + Create a file based on a selected template. + Create a file based on a selected template. + + src/app/features/template-export/template-export.module.ts + 48 + + entity context menu tooltip + + + Manage Export Templates + Manage Export Templates + + src/app/features/template-export/template-export.module.ts + 55 + + admin menu item + + + Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF. + Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF. + + src/app/features/template-export/template-export.module.ts + 108,116 + + TemplateExport + get signed agreement get signed agreement diff --git a/src/assets/locale/messages.xlf b/src/assets/locale/messages.xlf index 1dbe62e760..cd48135c3b 100644 --- a/src/assets/locale/messages.xlf +++ b/src/assets/locale/messages.xlf @@ -1719,6 +1719,10 @@ src/app/features/location/map-popup/map-popup.component.html 27 + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 44 + Button label @@ -2431,6 +2435,10 @@ src/app/core/config/config-fix.ts 856 + + src/app/features/template-export/template-export.entity.ts + 30 + Admin UI - Config Section Header form field label @@ -3331,6 +3339,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/features/file/edit-file/edit-file.component.html 20 + + src/app/features/file/edit-file/edit-file.component.html + 20 + placeholder for file-input @@ -3925,6 +3937,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/core/config/config-fix.ts 843 + + src/app/features/template-export/template-export.entity.ts + 39 + src/app/features/todos/model/todo-default-configs.ts 19 @@ -6715,6 +6731,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/features/file/edit-file/edit-file.component.html 22 + + src/app/features/file/edit-file/edit-file.component.html + 22 + Tooltip show file @@ -6723,6 +6743,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/features/file/edit-file/edit-file.component.html 45 + + src/app/features/file/edit-file/edit-file.component.html + 45 + src/app/features/file/edit-photo/edit-photo.component.html 28 @@ -6735,6 +6759,10 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit src/app/features/file/edit-file/edit-file.component.html 52 + + src/app/features/file/edit-file/edit-file.component.html + 52 + src/app/features/file/edit-photo/edit-photo.component.html 6 @@ -6744,7 +6772,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File too large. Usually files up to 5 MB are supported. src/app/features/file/edit-file/edit-file.component.ts - 130 + 138 File Upload Error Message @@ -6752,7 +6780,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Changes to file attachments are not available offline. src/app/features/file/edit-file/edit-file.component.ts - 132 + 140 File Upload Error Message @@ -6760,7 +6788,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Failed to update file attachment. Please try again. src/app/features/file/edit-file/edit-file.component.ts - 135 + 143 File Upload Error Message @@ -6768,7 +6796,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit File "" deleted src/app/features/file/edit-file/edit-file.component.ts - 185 + 195 Message for user @@ -7296,6 +7324,164 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Button to download data + + Generate File from Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 2 + + + + Select the template based on which to generate a file for the current record or configure available templates. + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 3,15 + + + + Template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 21 + + + + Select a file template + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 23 + + + + Generate File + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.html + 37 + + + src/app/features/template-export/template-export.module.ts + 46 + + + + Failed to generate document [] + + src/app/features/template-export/template-export-selection-dialog/template-export-selection-dialog.component.ts + 76 + + + + Export Template + + src/app/features/template-export/template-export.entity.ts + 17 + + TemplateExport + + + Export Templates + + src/app/features/template-export/template-export.entity.ts + 18 + + TemplateExport + + + Applicable Entity Types + + src/app/features/template-export/template-export.entity.ts + 48 + + TemplateExport + + + Entity Types + + src/app/features/template-export/template-export.entity.ts + 49 + + TemplateExport + + + Template File + + src/app/features/template-export/template-export.entity.ts + 59 + + TemplateExport + + + Upload a specially prepared document that contains placeholders, which will be replace with actual data from a specific entity when generating a PDF. + + src/app/features/template-export/template-export.entity.ts + 60 + + TemplateExport + + + File name pattern for generated file + + src/app/features/template-export/template-export.entity.ts + 83 + + TemplateExport + + + File name pattern + + src/app/features/template-export/template-export.entity.ts + 84 + + TemplateExport + + + The filename for the resulting file when using this template. You can use the same placeholders here as in the template file itself (e.g. "my-report_{d.name}.pdf"). + + src/app/features/template-export/template-export.entity.ts + 85 + + TemplateExport + + + Create a file based on a selected template. + + src/app/features/template-export/template-export.module.ts + 48 + + entity context menu tooltip + + + Manage Export Templates + + src/app/features/template-export/template-export.module.ts + 55 + + admin menu item + + + Upload a specially prepared template file here. +The file can contain placeholders that will be replaced with actual data when a file is generated for a selected record. +For example {d.name} will be replaced with the value in the "name" field of the given entity. +See the documentation of the [carbone system](https://carbone.io/documentation.html#substitutions) for more information. + +The placeholder keys must match the field "Field ID" of the record data structure in Aam Digital. +You can find this in the Admin UI form builder (Edit Data Structure -> Details View) and edit a specific field to view its details. + +Template files can be in most office document formats (odt, docx, ods, xlsx, odp, pptx) or PDF. + + src/app/features/template-export/template-export.module.ts + 108,116 + + TemplateExport + get signed agreement From b49fa126a6a95edab970588213028fc7fa25af47 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Wed, 18 Sep 2024 11:22:23 +0200 Subject: [PATCH 07/11] fix(Admin UI): make entity color configurable --- .../admin-entity-general-settings.component.html | 13 +++++++++++++ .../admin-entity-general-settings.component.ts | 1 + .../admin-entity/admin-entity.component.spec.ts | 1 + .../admin/admin-entity/admin-entity.component.ts | 1 + 4 files changed, 16 insertions(+) diff --git a/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html b/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html index 8ba6e8b2c9..8c5f76d64e 100644 --- a/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html +++ b/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html @@ -53,6 +53,19 @@

General Settings of "{{ entityConstructor.label }}" Records

> + + + + Color +   + + + +
diff --git a/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.ts b/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.ts index 34918d1c43..5d8a2728ba 100644 --- a/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.ts +++ b/src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.ts @@ -90,6 +90,7 @@ export class AdminEntityGeneralSettingsComponent implements OnInit { label: [this.generalSettings.label, Validators.required], labelPlural: [this.generalSettings.labelPlural], icon: [this.generalSettings.icon], + color: [this.generalSettings.color], toStringAttributes: [this.generalSettings.toStringAttributes], hasPII: [this.generalSettings.hasPII], }); diff --git a/src/app/core/admin/admin-entity/admin-entity.component.spec.ts b/src/app/core/admin/admin-entity/admin-entity.component.spec.ts index b7d7ba316b..ac76988914 100644 --- a/src/app/core/admin/admin-entity/admin-entity.component.spec.ts +++ b/src/app/core/admin/admin-entity/admin-entity.component.spec.ts @@ -156,6 +156,7 @@ describe("AdminEntityComponent", () => { label: "Admin Test", labelPlural: "Admin Test", icon: undefined, + color: undefined, toStringAttributes: ["entityId"], hasPII: false, attributes: jasmine.objectContaining({ diff --git a/src/app/core/admin/admin-entity/admin-entity.component.ts b/src/app/core/admin/admin-entity/admin-entity.component.ts index dc9adffec5..a1f76cf795 100644 --- a/src/app/core/admin/admin-entity/admin-entity.component.ts +++ b/src/app/core/admin/admin-entity/admin-entity.component.ts @@ -179,6 +179,7 @@ export class AdminEntityComponent implements OnInit { entitySchemaConfig.label = this.configEntitySettings.label; entitySchemaConfig.labelPlural = this.configEntitySettings.labelPlural; entitySchemaConfig.icon = this.configEntitySettings.icon; + entitySchemaConfig.color = this.configEntitySettings.color; entitySchemaConfig.toStringAttributes = this.configEntitySettings.toStringAttributes; entitySchemaConfig.hasPII = this.configEntitySettings.hasPII; From acae4a1482f1360da8a08c1c3570bb0cfce8e391 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Wed, 2 Oct 2024 11:50:13 +0530 Subject: [PATCH 08/11] fix: catch potential errors when working with broken IDs --- src/app/core/entity/model/entity.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app/core/entity/model/entity.ts b/src/app/core/entity/model/entity.ts index 99316a8550..b3de68235b 100644 --- a/src/app/core/entity/model/entity.ts +++ b/src/app/core/entity/model/entity.ts @@ -25,6 +25,7 @@ import { import { IconName } from "@fortawesome/fontawesome-svg-core"; import { UpdateMetadata } from "./update-metadata"; import { EntityBlockConfig } from "../../basic-datatypes/entity/entity-block/entity-block-config"; +import { Logging } from "../../logging/logging.service"; /** * This represents a static class of type . @@ -149,8 +150,14 @@ export class Entity { * @param id An entity's id including prefix. */ static extractEntityIdFromId(id: string): string { - const split = id.indexOf(":"); - return id.substring(split + 1); + let type: string = undefined; + try { + const split = id.indexOf(":"); + type = id.substring(split + 1); + } catch (e) { + Logging.debug("Error extracting entityId from id", id, e); + } + return type; } /** From 1b396b489ef0f696d39b3902fd989cea275bb2d3 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 9 Sep 2024 13:04:42 +0200 Subject: [PATCH 09/11] docs: add SQL snippets link to reports.md --- doc/compodoc_sources/how-to-guides/reports.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/compodoc_sources/how-to-guides/reports.md b/doc/compodoc_sources/how-to-guides/reports.md index a95aae02cb..6dbc1d125a 100644 --- a/doc/compodoc_sources/how-to-guides/reports.md +++ b/doc/compodoc_sources/how-to-guides/reports.md @@ -28,6 +28,8 @@ There are different modes for `ReportConfig`: ### sql Requirements: SQS +Some useful SQL query snippets for Aam Digital contexts are collected here: [Aam Digital SQL Snippets](https://docs.google.com/document/d/14JqS6xgZzC1xHUogDho5n1kyNLzX1Eoyv6MeZtRejuM/edit?usp=sharing) + #### Simple SQL Report ```json From ddfc7e48e2775840dbb4028aca3d7e84d0561ccc Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Wed, 2 Oct 2024 19:17:46 +0530 Subject: [PATCH 10/11] fix: update dependencies (software packages) --- package-lock.json | 1432 ++++++++++++++++++--------------------------- package.json | 62 +- 2 files changed, 616 insertions(+), 878 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5e987f135..8d892cf2ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,30 +9,30 @@ "version": "0.0.0", "license": "GPL-3.0", "dependencies": { - "@angular/animations": "^18.2.0", - "@angular/cdk": "^18.2.0", - "@angular/common": "^18.2.0", - "@angular/compiler": "^18.2.0", - "@angular/core": "^18.2.0", - "@angular/forms": "^18.2.0", - "@angular/localize": "^18.2.0", - "@angular/material": "^18.2.0", - "@angular/material-moment-adapter": "^18.2.0", - "@angular/platform-browser": "^18.2.0", - "@angular/platform-browser-dynamic": "^18.2.0", - "@angular/router": "^18.2.0", - "@angular/service-worker": "^18.2.0", + "@angular/animations": "^18.2.7", + "@angular/cdk": "^18.2.6", + "@angular/common": "^18.2.7", + "@angular/compiler": "^18.2.7", + "@angular/core": "^18.2.7", + "@angular/forms": "^18.2.7", + "@angular/localize": "^18.2.7", + "@angular/material": "^18.2.6", + "@angular/material-moment-adapter": "^18.2.6", + "@angular/platform-browser": "^18.2.7", + "@angular/platform-browser-dynamic": "^18.2.7", + "@angular/router": "^18.2.7", + "@angular/service-worker": "^18.2.7", "@aytek/material-color-picker": "^1.0.4", "@casl/ability": "^6.7.1", - "@casl/angular": "^8.2.7", + "@casl/angular": "^8.2.8", "@faker-js/faker": "^8.4.1", "@fortawesome/angular-fontawesome": "^0.15.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@ngneat/until-destroy": "^10.0.0", - "@sentry/angular": "^8.26.0", - "angulartics2": "^14.0.0", + "@sentry/angular": "^8.32.0", + "angulartics2": "^14.1.0", "assert": "^2.1.0", "crypto-es": "^2.1.0", "deep-object-diff": "^1.1.9", @@ -52,27 +52,27 @@ "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "stream-browserify": "^3.0.0", - "tslib": "^2.6.3", + "tslib": "^2.7.0", "util": "^0.12.5", "uuid": "^10.0.0", "webpack": "^5.94.0", "zone.js": "^0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.0", - "@angular-eslint/builder": "^18.3.0", - "@angular-eslint/eslint-plugin": "^18.3.0", - "@angular-eslint/eslint-plugin-template": "^18.3.0", - "@angular-eslint/schematics": "^18.3.0", - "@angular-eslint/template-parser": "^18.3.0", - "@angular/cli": "^18.2.0", - "@angular/compiler-cli": "^18.2.0", - "@babel/core": "^7.24.9", + "@angular-devkit/build-angular": "^18.2.7", + "@angular-eslint/builder": "^18.3.1", + "@angular-eslint/eslint-plugin": "^18.3.1", + "@angular-eslint/eslint-plugin-template": "^18.3.1", + "@angular-eslint/schematics": "^18.3.1", + "@angular-eslint/template-parser": "^18.3.1", + "@angular/cli": "^18.2.7", + "@angular/compiler-cli": "^18.2.7", + "@babel/core": "^7.25.2", "@compodoc/compodoc": "^1.1.25", "@cypress/schematic": "~2.5.2", - "@percy/cli": "^1.29.0", + "@percy/cli": "^1.29.4", "@percy/storybook": "^5.0.3", - "@schematics/angular": "^18.2.1", + "@schematics/angular": "^18.2.7", "@storybook/addon-actions": "^7.6.20", "@storybook/addon-essentials": "^7.6.20", "@storybook/angular": "^7.6.20", @@ -89,13 +89,13 @@ "@typescript-eslint/parser": "^8.2.0", "babel-loader": "^9.1.3", "cypress": "~13.14.0", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-storybook": "^0.8.0", - "jasmine-core": "^5.2.0", + "jasmine-core": "^5.3.0", "jasmine-spec-reporter": "^7.0.0", - "karma": "^6.4.3", + "karma": "^6.4.4", "karma-chrome-launcher": "^3.2.0", "karma-cli": "^2.0.0", "karma-coverage": "^2.2.1", @@ -112,15 +112,6 @@ "xliff": "^6.2.1" } }, - "node_modules/@adobe/css-tools": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", - "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@aduh95/viz.js": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.4.0.tgz", @@ -140,13 +131,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.0.tgz", - "integrity": "sha512-s1atTSL98XLUUxfWEQAnhFaOFIJZDLWjSqec+Sb+f4iZFj+hOFejzJxPVnHMIJudOzn0Lqjk3t987KG/zNEGdg==", + "version": "0.1802.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.7.tgz", + "integrity": "sha512-kpcgXnepEXcoxDTbqbGj7Hg1WJLWj1HLR3/FKmC5TbpBf1xiLxiqfkQNwz3BbE/W9JWMLdrXr3GI9O3O2gWPLg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", + "@angular-devkit/core": "18.2.7", "rxjs": "7.8.1" }, "engines": { @@ -156,17 +147,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.0.tgz", - "integrity": "sha512-V0XKT7xt6d6duXqmDAQEQgEJNXuWAekpHEDxafvBT0MTxcEhu0ozQVwI4oAekiKOz+APIcAZyMzvw3Tlzog5cw==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.7.tgz", + "integrity": "sha512-u8PriYdgddK7k+OS/pOFPD1v4Iu5bztUJZXZVcGeXBZFFdnGFFzKmQw9mfcyGvTMJp2ABgBuuJT0YqYgNfAhzw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.0", - "@angular-devkit/build-webpack": "0.1802.0", - "@angular-devkit/core": "18.2.0", - "@angular/build": "18.2.0", + "@angular-devkit/architect": "0.1802.7", + "@angular-devkit/build-webpack": "0.1802.7", + "@angular-devkit/core": "18.2.7", + "@angular/build": "18.2.7", "@babel/core": "7.25.2", "@babel/generator": "7.25.0", "@babel/helper-annotate-as-pure": "7.24.7", @@ -177,7 +168,7 @@ "@babel/preset-env": "7.25.3", "@babel/runtime": "7.25.0", "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.0", + "@ngtools/webpack": "18.2.7", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -209,7 +200,7 @@ "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.77.8", + "sass": "1.77.6", "sass-loader": "16.0.0", "semver": "7.6.3", "source-map-loader": "5.0.0", @@ -217,10 +208,10 @@ "terser": "5.31.6", "tree-kill": "1.2.2", "tslib": "2.6.3", - "vite": "5.4.0", + "vite": "5.4.6", "watchpack": "2.4.1", - "webpack": "5.93.0", - "webpack-dev-middleware": "7.3.0", + "webpack": "5.94.0", + "webpack-dev-middleware": "7.4.2", "webpack-dev-server": "5.0.4", "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" @@ -294,31 +285,6 @@ "node": ">=14.17.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/css-loader": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", @@ -368,28 +334,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/is-wsl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", @@ -423,18 +367,6 @@ "node": ">=10" } }, - "node_modules/@angular-devkit/build-angular/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/@angular-devkit/build-angular/node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -443,9 +375,9 @@ "license": "MIT" }, "node_modules/@angular-devkit/build-angular/node_modules/memfs": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", - "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.12.0.tgz", + "integrity": "sha512-74wDsex5tQDSClVkeK1vtxqYCAgCoXxx+K4NSHzgU/muYVYByFqa+0RnrPO9NM6naWm1+G9JmZ0p6QHhXmeYfA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -481,57 +413,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "node_modules/@angular-devkit/build-angular/node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", "dev": true, + "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { - "webpack": "bin/webpack.js" + "sass": "sass.js" }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "node": ">=14.0.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-middleware": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.3.0.tgz", - "integrity": "sha512-xD2qnNew+F6KwOGZR7kWdbIou/ud7cVqLEXeK1q0nHcNsX/u7ul/fSdlOTX4ntSL5FNFy7ZJJXbf0piF591JYw==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", "dev": true, "license": "MIT", "dependencies": { @@ -558,32 +468,14 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.0.tgz", - "integrity": "sha512-bU7AxlI/avnlOLrgE195cokrOA1ayT6JjRv8Hxzh1bIOa1jE87HsyjxvQhOLcdEb97zwHqMqbntcgUNBgsegJQ==", + "version": "0.1802.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.7.tgz", + "integrity": "sha512-VrtbrhZ+dht3f0GjtfRLRGRN4XHN/W+/bA9DqckdxVS6SydsrCWNHonvEPmOs4jJmGIGXIu6tUBMcWleTao2sg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/architect": "0.1802.7", "rxjs": "7.8.1" }, "engines": { @@ -597,9 +489,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.0.tgz", - "integrity": "sha512-8SOopyUKUMqAq2rj32XkTIfr79Y274k4uglxxRtzHYoWwHlLdG0KrA+wCcsh/FU9PyR4dA+5dcDAApn358ZF+Q==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.7.tgz", + "integrity": "sha512-1ZTi4A6tEC2bkJ/puCIdIPYhesnlCVOMSDJL/lZAd0hC6X22T4pwu0AEvue7mcP5NbXpQDiBaXOZ3MmCA8PwOA==", "dev": true, "license": "MIT", "dependencies": { @@ -631,13 +523,13 @@ "dev": true }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.0.tgz", - "integrity": "sha512-WWKwz2RKMVI6T25JFwOSSfRLB+anNzabVIRwf85R/YMIo34BUk777f2JU/7cCKlxSpQu39TqIfMQZAyzAD1z0A==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.7.tgz", + "integrity": "sha512-j7198lpkOXMG+Gyfln/5aDgBZV7m4pWMzHFhkO3+w3cbCNUN1TVZW0SyJcF+CYaxANzTbuumfvpsYc/fTeAGLw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", + "@angular-devkit/core": "18.2.7", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -657,9 +549,9 @@ "license": "MIT" }, "node_modules/@angular-eslint/builder": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-18.3.0.tgz", - "integrity": "sha512-httEQyqyBw3+0CRtAa7muFxHrauRfkEfk/jmrh5fn2Eiu+I53hAqFPgrwVi1V6AP/kj2zbAiWhd5xM3pMJdoRQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-18.3.1.tgz", + "integrity": "sha512-cPc7Ye9zDs5M4i+feL6vob+mh7yX5vxvOS5KQIhneUrp5e9D+IGuNFMmBLlOPpmklSc9XJBtuvI5Zjuh4z1ETw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -668,21 +560,21 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.3.0.tgz", - "integrity": "sha512-v/59FxUKnMzymVce99gV43huxoqXWMb85aKvzlNvLN+ScDu6ZE4YMiTQNpfapVL2lkxhs0uwB3jH17EYd5TcsA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.3.1.tgz", + "integrity": "sha512-sikmkjfsXPpPTku1aQkQ1MNNEKGBgGGRvUN/WeNS9dhCJ4dxU3O7dZctt1aQWj+W3nbuUtDiimAWF5fZHGFE2Q==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-18.3.0.tgz", - "integrity": "sha512-Vl7gfPMXxvtHTjYdlzR161aj5xrqW6T57wd8ToQ7Gqzm0qHGfY6kE4SQobUa2LCYckTNSlv+zXe48C4ah/dSjw==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-18.3.1.tgz", + "integrity": "sha512-MP4Nm+SHboF8KdnN0KpPEGAaTTzDLPm3+S/4W3Mg8onqWCyadyd4mActh9mK/pvCj8TVlb/SW1zeTtdMYhwonw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.3.0", - "@angular-eslint/utils": "18.3.0" + "@angular-eslint/bundled-angular-compiler": "18.3.1", + "@angular-eslint/utils": "18.3.1" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -691,14 +583,14 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.3.0.tgz", - "integrity": "sha512-ddR/qwYbUeq9IpyVKrPbfZyRBTy6V8uc5I0JcBKttQ4CZ4joXhqsVgWFsI+JAMi8E66uNj1VC7NuKCOjDINv2Q==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.3.1.tgz", + "integrity": "sha512-hBJ3+f7VSidvrtYaXH7Vp0sWvblA9jLK2c6uQzhYGWdEDUcTg7g7VI9ThW39WvMbHqkyzNE4PPOynK69cBEDGg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.3.0", - "@angular-eslint/utils": "18.3.0", + "@angular-eslint/bundled-angular-compiler": "18.3.1", + "@angular-eslint/utils": "18.3.1", "aria-query": "5.3.0", "axobject-query": "4.1.0" }, @@ -709,14 +601,14 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-18.3.0.tgz", - "integrity": "sha512-rQ4DEWwf3f5n096GAK6JvXD0SRzRJ52WRaIyKg8MMkk6qvUDfZI8seOkcbjDtZoIe6Ds7DfqSfJgNVte75qvPQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-18.3.1.tgz", + "integrity": "sha512-BTsQHDu7LjvXannJTb5BqMPCFIHRNN94eRyb60VfjJxB/ZFtsbAQDFFOi5lEZsRsd4mBeUMuL9mW4IMcPtUQ9Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/eslint-plugin": "18.3.0", - "@angular-eslint/eslint-plugin-template": "18.3.0", + "@angular-eslint/eslint-plugin": "18.3.1", + "@angular-eslint/eslint-plugin-template": "18.3.1", "ignore": "5.3.2", "semver": "7.6.3", "strip-json-comments": "3.1.1" @@ -727,13 +619,13 @@ } }, "node_modules/@angular-eslint/template-parser": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-18.3.0.tgz", - "integrity": "sha512-1mUquqcnugI4qsoxcYZKZ6WMi6RPelDcJZg2YqGyuaIuhWmi3ZqJZLErSSpjP60+TbYZu7wM8Kchqa1bwJtEaQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-18.3.1.tgz", + "integrity": "sha512-JUUkfWH1G+u/Uk85ZYvJSt/qwN/Ko+jlXFtzBEcknJZsTWTwBcp36v77gPZe5FmKSziJZpyPUd+7Kiy6tuSCTw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.3.0", + "@angular-eslint/bundled-angular-compiler": "18.3.1", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -742,13 +634,13 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-18.3.0.tgz", - "integrity": "sha512-sCrkHkpxBJZLuCikdboZoawCfc2UgbJv+T14tu2uQCv+Vwzeadnu04vkeY2vTkA8GeBdBij/G9/N/nvwmwVw3g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-18.3.1.tgz", + "integrity": "sha512-sd9niZI7h9H2FQ7OLiQsLFBhjhRQTASh+Q0+4+hyjv9idbSHBJli8Gsi2fqj9zhtMKpAZFTrWzuLUpubJ9UYbA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.3.0" + "@angular-eslint/bundled-angular-compiler": "18.3.1" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -757,9 +649,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.0.tgz", - "integrity": "sha512-BFAfqDDjsa0Q91F4s33pFcBG+ydFDurEmwYGG1gmO7UXZJI6ZbRVdULaZHz75M+Bf3hJkzVB05pesvfbK+Fg/g==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.7.tgz", + "integrity": "sha512-5B7qD1K+kKOf9lgJT4VNMft3IK2BnRHjN1S6l38ywzQ/nxpmCG7f+qKAAU6CpCywhNUBeXW0hVXTMuMNPVOcQQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -768,18 +660,18 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0" + "@angular/core": "18.2.7" } }, "node_modules/@angular/build": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.0.tgz", - "integrity": "sha512-LvNJ2VOEVy3N1tGzt+xnKyweRBuUE1NsnuEEWAb9Y+V1cyRgj0s7FX2+IQZZQhP+W/pXfbsKaByOAbJ5KWV85Q==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.7.tgz", + "integrity": "sha512-oq6JsVxLP9/w9F2IjKroJwPB9CdlMblu2Xhfq/qQZRSUuM8Ppt1svr2FBTo1HrLIbosqukkVcSSdmKYDneo+cg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/architect": "0.1802.7", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -798,10 +690,10 @@ "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", "piscina": "4.6.1", - "rollup": "4.20.0", - "sass": "1.77.8", + "rollup": "4.22.4", + "sass": "1.77.6", "semver": "7.6.3", - "vite": "5.4.0", + "vite": "5.4.6", "watchpack": "2.4.1" }, "engines": { @@ -857,9 +749,9 @@ } }, "node_modules/@angular/build/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -916,9 +808,9 @@ } }, "node_modules/@angular/build/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -1046,6 +938,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@angular/build/node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@angular/build/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -1129,9 +1039,9 @@ } }, "node_modules/@angular/cdk": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.0.tgz", - "integrity": "sha512-hjuUWNhxU48WB2i1j4NLwnPTaCnucRElfp7DBX5Io0rY5Lwl3HXafvyi7/A1D0Ah+YsJpktKOWPDGv8r7vxymg==", + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.6.tgz", + "integrity": "sha512-Gfq/iv4zhlKYpdQkDaBRwxI71NHNUHM1Cs1XhnZ0/oFct5HXvSv1RHRGTKqBJLLACaAPzZKXJ/UglLoyO5CNiQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1146,18 +1056,18 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.0.tgz", - "integrity": "sha512-hA60QIA7Dh8LQxM42wqd7WrhwQjbjB8oIRH5Slgbiv8iocAo76scp1/qyZo2SSzjfkB7jxUiao5L4ckiJ/hvZg==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.7.tgz", + "integrity": "sha512-KoWgSvhRsU05A2m6B7jw1kdpyoS+Ce5GGLW6xcnX7VF2AckW54vYd/8ZkgpzQrKfvIpVblYd4KJGizKoaLZ5jA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.0", - "@angular-devkit/core": "18.2.0", - "@angular-devkit/schematics": "18.2.0", + "@angular-devkit/architect": "0.1802.7", + "@angular-devkit/core": "18.2.7", + "@angular-devkit/schematics": "18.2.7", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.0", + "@schematics/angular": "18.2.7", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -1179,23 +1089,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@angular/cli/node_modules/@schematics/angular": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.0.tgz", - "integrity": "sha512-XePvx2ZnxCcAQw5lHVMUrJvm8MXqAWGcMyJDAuQUqNZrPCk3GpCaplWx2n+nPkinYVX2Q2v/DqtvWStQwgU4nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "18.2.0", - "@angular-devkit/schematics": "18.2.0", - "jsonc-parser": "3.3.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@angular/cli/node_modules/ansi-escapes": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", @@ -1492,9 +1385,9 @@ } }, "node_modules/@angular/common": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.0.tgz", - "integrity": "sha512-DELx/QYNqqjmiM+kE5PoVmyG4gPw5WB1bDDeg3hEuBCK3eS2KosgQH0/MQo3OSBZHOcAMFjfHMGqKgxndmYixQ==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.7.tgz", + "integrity": "sha512-5vDBmBR2JcIxHVEDunKXNU+T+OvTGiHZTSo35GFOHJxKFgX5g6+0tJBZunK04oBZGbJQUmp3pg2kMvuKKjZnkQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1503,14 +1396,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0", + "@angular/core": "18.2.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.0.tgz", - "integrity": "sha512-RmGwQ7jRzotUKKMk0CwxTcIXFr5mRxSbJf9o5S3ujuIOo1lYop8SQZvjq67a5BuoYyD+1pX6iMmxZqlbKoihBQ==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.7.tgz", + "integrity": "sha512-XemlYyRGnu/HrICtXwTPmGtyOrI8BhbGg/HMiJ7sVx40AeEIX0uyDgnu9Gc5OjmtDqZZ8Qftg1sQAxaCVjLb1w==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1519,7 +1412,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0" + "@angular/core": "18.2.7" }, "peerDependenciesMeta": { "@angular/core": { @@ -1528,9 +1421,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.0.tgz", - "integrity": "sha512-pPBFjMqNTNettrleLtEc6a/ysOZjG3F0Z5lyKYePcyNQmn2rpa9XmD2y6PhmzTmIhxeXrogWA84Xgg/vK5wBNw==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.7.tgz", + "integrity": "sha512-U7cveObj+rrXH5EC8egAhATCeAAcOceEQDTVIOWmBa0qMR4hOMjtI2XUS2QRuI1Q+fQZ2hVEOW95WVLvEMsANA==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -1551,14 +1444,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.0", + "@angular/compiler": "18.2.7", "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.0.tgz", - "integrity": "sha512-7+4wXfeAk1TnG3MGho2gpBI5XHxeSRWzLK2rC5qyyRbmMV+GrIgf1HqFjQ4S02rydkQvGpjqQHtO1PYJnyn4bg==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.7.tgz", + "integrity": "sha512-hLOxgxLiyWm9iVHBsUsJfx1hDsXWZnfJBlr+N7cev53f0CDoPfbshqq6KV+JFqXFDguzR9dKHm1ewT1jK3e6Tw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1572,9 +1465,9 @@ } }, "node_modules/@angular/forms": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.0.tgz", - "integrity": "sha512-G+4BjNCUo4cRwg9NwisGLbtG/1AbIJNOO3RUejPJJbCcAkYMSzmDWSQ+LQEGW4vC/1xaDKO8AT71DI/I09bOxA==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.7.tgz", + "integrity": "sha512-WO3c9/OA7ekBnDBgmvi5TlHshOt5S4NREIP+/VVyuRgg28BwUWyO/Nqh19nguE1UNNRt6OMLkT6NSV2ewhcXUg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1583,16 +1476,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0", + "@angular/common": "18.2.7", + "@angular/core": "18.2.7", + "@angular/platform-browser": "18.2.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.0.tgz", - "integrity": "sha512-ul8yGmimiHkhUU87isDCst0790jTBHP1zPBMI2q7QHv7iDzSN5brV8zUMcRypxhh4mQOCnq2LtP84o5ybn4Sig==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.7.tgz", + "integrity": "sha512-qYozomhO+1BlvtoMEEgKhaKz8thoztqNZEYPq9RmfkTB5uW7Q8h6rr1Sc2YAzJ6+ZA0McwabdJSX1TDxWyZx0Q==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -1609,21 +1502,21 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.0", - "@angular/compiler-cli": "18.2.0" + "@angular/compiler": "18.2.7", + "@angular/compiler-cli": "18.2.7" } }, "node_modules/@angular/material": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.0.tgz", - "integrity": "sha512-lOXk8pAVP4Mr0/Q6YrNnVYQVTnR8aEG5D9QSEWjs+607gONuh/9n7ERBdzX7xO9D0vYyXq+Vil32zcF41/Q8Cg==", + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.6.tgz", + "integrity": "sha512-ObxC/vomSb9QF3vIztuiInQzws+D6u09Dhfx6uNFjtyICqxEFpF7+Qx7QVDWrsuXOgxZTKgacK8f46iV8hWUfg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.2.0", + "@angular/cdk": "18.2.6", "@angular/common": "^18.0.0 || ^19.0.0", "@angular/core": "^18.0.0 || ^19.0.0", "@angular/forms": "^18.0.0 || ^19.0.0", @@ -1632,23 +1525,23 @@ } }, "node_modules/@angular/material-moment-adapter": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-18.2.0.tgz", - "integrity": "sha512-nyQl8ktWQvEIziiG35F6ObflSpsPVGeV3n6kemvmkOl6Q7T1r1B01B+b7ouHkd47Fq+YFgfkxHl2eDUt9XwJCA==", + "version": "18.2.6", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-18.2.6.tgz", + "integrity": "sha512-9RwdilTesMBAxIEeY6JzMpWIOU+dS4Ned/1wdRQN33x3Qb/POhyqAv+3XfeLqoJyn7GXkl6dHFu8VEE/1WYaKg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/core": "^18.0.0 || ^19.0.0", - "@angular/material": "18.2.0", + "@angular/material": "18.2.6", "moment": "^2.18.1" } }, "node_modules/@angular/platform-browser": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.0.tgz", - "integrity": "sha512-yhj281TuPz5a8CehwucwIVl29Oqte9KS4X/VQkMV++GpYQE2KKKcoff4FXSdF5RUcUYkK2li4IvawIqPmUSagg==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.7.tgz", + "integrity": "sha512-xgj2DH/isFrMZ73dJJm89NRnWBI3AHtugQrZbIapkKBdEt/C1o4SR2W2cV4mPb9o+ELnWurfrxFt9o/q2vnVLw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1657,9 +1550,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.0", - "@angular/common": "18.2.0", - "@angular/core": "18.2.0" + "@angular/animations": "18.2.7", + "@angular/common": "18.2.7", + "@angular/core": "18.2.7" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1668,9 +1561,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.0.tgz", - "integrity": "sha512-izfaXKNC/kqOEzJG8eTnFu39VLI3vv3dTKoYOdEKRB7MTI0t0x66oZmABnHcihtkTSvXs/is+7lA5HmH2ZuIEQ==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.7.tgz", + "integrity": "sha512-BDldzUKjnUjo0NW5gHjBY6CeJP1bWVfF1h/T3idyYG+F4Lxlb3aykRgLWXg4srNLY1KqE7XOYUmgc5cV613bgw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1679,16 +1572,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/compiler": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0" + "@angular/common": "18.2.7", + "@angular/compiler": "18.2.7", + "@angular/core": "18.2.7", + "@angular/platform-browser": "18.2.7" } }, "node_modules/@angular/router": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.0.tgz", - "integrity": "sha512-6/462hvC3HSwbps8VItECHpkdkiFqRmTKdn1Trik+FjnvdupYrKB6kBsveM3eP+gZD4zyMBMKzBWB9N/xA1clw==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.7.tgz", + "integrity": "sha512-TXE8Aw63hDp3PEaNu4B1DMNvlS0uCzs36o/OSCCmewmLnzyJygkgi4jeEj20FsWPAQOUj5g5tnCYgxz1IRrCUg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1697,16 +1590,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0", + "@angular/common": "18.2.7", + "@angular/core": "18.2.7", + "@angular/platform-browser": "18.2.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.0.tgz", - "integrity": "sha512-ngcALrgqMuAeIo5dgou6eBzdtgLvmVg5zwmZuTyrnNPZENEaKTj7u5pm9++gl62797sUWlMbL+fa/BOhntGs5A==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.7.tgz", + "integrity": "sha512-1t8PUWmZi32i/SG/r12vz+cfn0l3xVEa0FY7GXaZK7hlfDL34js1HZXHkvGUuRZRw/4L1jl7AwPoxwGeWr2ldg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1718,8 +1611,8 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0" + "@angular/common": "18.2.7", + "@angular/core": "18.2.7" } }, "node_modules/@aw-web-design/x-default-browser": { @@ -3758,9 +3651,10 @@ } }, "node_modules/@casl/angular": { - "version": "8.2.7", - "resolved": "https://registry.npmjs.org/@casl/angular/-/angular-8.2.7.tgz", - "integrity": "sha512-ZQBre3azRonTCKvH31uzKMlRsARM9aez9xgWwEAvbL80fwFtY3Nu4fy6o0nvagiaE8KB9cHKIJH5to8KwUGKsg==", + "version": "8.2.8", + "resolved": "https://registry.npmjs.org/@casl/angular/-/angular-8.2.8.tgz", + "integrity": "sha512-2PapFaNboZv35EJH8HmIjKDMKkF8WDpMK9WhLOcAas8d9D6s9bsByB3KJyPkz5ZKDLOyj3d0mleyWJakQXzObQ==", + "license": "MIT", "peerDependencies": { "@angular/core": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "@casl/ability": "^3.0.0 || ^4.0.0 || ^5.1.0 || ^6.0.0", @@ -4766,9 +4660,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -4939,14 +4833,14 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -5932,9 +5826,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.0.tgz", - "integrity": "sha512-a6hbkYzh/KUlI52huiU4vztqIuxzyddg6kJGcelUJx3Ju6MJeziu+XmJ6wqFRvfH89zmJeaSADKsGFQaBHtJLg==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.7.tgz", + "integrity": "sha512-BmnFxss6zGobGyq9Mi7736golbK8RLgF+zYCQZ+4/OfMMA1jKVoELnyJqNyAx+DQn3m1qKVBjtGEL7pTNpPzOw==", "dev": true, "license": "MIT", "engines": { @@ -6211,20 +6105,21 @@ } }, "node_modules/@percy/cli": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.29.3.tgz", - "integrity": "sha512-j+LEHQrrtQV0uOe1u38U6RExPl86rwof+qWtkV8cOvPwucAo0DmeVfperLhZFIU/qrZjA9lHkDdYHZyzRndOBw==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.29.4.tgz", + "integrity": "sha512-OyKWT6jgr+GfQ1Pu/oOc+MAPlgMjAlWKQYeiyqUNjG9jJs61h2j4kDVUfOhVYZvBfNAW9NI0yJyAAuGWYnNy7g==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-app": "1.29.3", - "@percy/cli-build": "1.29.3", - "@percy/cli-command": "1.29.3", - "@percy/cli-config": "1.29.3", - "@percy/cli-exec": "1.29.3", - "@percy/cli-snapshot": "1.29.3", - "@percy/cli-upload": "1.29.3", - "@percy/client": "1.29.3", - "@percy/logger": "1.29.3" + "@percy/cli-app": "1.29.4", + "@percy/cli-build": "1.29.4", + "@percy/cli-command": "1.29.4", + "@percy/cli-config": "1.29.4", + "@percy/cli-exec": "1.29.4", + "@percy/cli-snapshot": "1.29.4", + "@percy/cli-upload": "1.29.4", + "@percy/client": "1.29.4", + "@percy/logger": "1.29.4" }, "bin": { "percy": "bin/run.cjs" @@ -6234,39 +6129,42 @@ } }, "node_modules/@percy/cli-app": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.29.3.tgz", - "integrity": "sha512-7yGEDFIRLMsJ6nzl6aMem9nMj5rfkxODEQIciuIcuao5ZD1x23KhuN3u4QLLwQFOFgy7h4WAePnUTCR6ZtpGCQ==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-app/-/cli-app-1.29.4.tgz", + "integrity": "sha512-7wuOYnjnUAKkOpSC9sXZGJXNaLPoduJWJhNLupGyc/t1srgML8kAqKQ3jnwnKZQtPJv12w2bdWcmMpyQFK7k9A==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.29.3", - "@percy/cli-exec": "1.29.3" + "@percy/cli-command": "1.29.4", + "@percy/cli-exec": "1.29.4" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-build": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.29.3.tgz", - "integrity": "sha512-fvDr4mUFIG/TQmMWnzQqWi2ga57SWPzXwlh65a4/0PPRKo0dKybFhvZvhCFYhcnVWqXEVYRHM21/oUvFhgnsCw==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-build/-/cli-build-1.29.4.tgz", + "integrity": "sha512-4Zwr8mQgFqNdfnIl8RGEBSxBIAOBv2dHDTVFaoq51b4OGdYVbUMZw9+rE+qDojAE479paP55P/NO2bl3cDE8jA==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.29.3" + "@percy/cli-command": "1.29.4" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-command": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.29.3.tgz", - "integrity": "sha512-jSltYf97E5u4jjTaW6gLU+5T/sRBwS0RlvraE21gKa0N9SH1l5nWs4YFfsIwamYg3EnCmIrwAf0gupQcQgAuaA==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-command/-/cli-command-1.29.4.tgz", + "integrity": "sha512-ShT9b/mMkWq1SYlFVV5pNtWxSShMaxhFMhbQOoHDwthtOfemBR/qk4wkry0R2dBfn8ShIoAwFQy4AuA4krNuzw==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/config": "1.29.3", - "@percy/core": "1.29.3", - "@percy/logger": "1.29.3" + "@percy/config": "1.29.4", + "@percy/core": "1.29.4", + "@percy/logger": "1.29.4" }, "bin": { "percy-cli-readme": "bin/readme.js" @@ -6276,25 +6174,27 @@ } }, "node_modules/@percy/cli-config": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.29.3.tgz", - "integrity": "sha512-lVrOqeS1ACaQp9tM4LE7joW/cuGaSJcM/182ci5D3Zr9Yz6Ik/oe1MQnIM9dqnsHmnwasuGBsICsflbVqqcXHQ==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-config/-/cli-config-1.29.4.tgz", + "integrity": "sha512-4DxoVYOBHw5jEho1Qu0ipyZZobyNlA1dTjPwiTvIFynkui3sy+0+r2j5o4JiP1a1rbmPRsm9jZQ7y8PhQ/6Yzw==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.29.3" + "@percy/cli-command": "1.29.4" }, "engines": { "node": ">=14" } }, "node_modules/@percy/cli-exec": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.29.3.tgz", - "integrity": "sha512-AfZ2hI/snahjgXHuI0bKhRLJoEOfod8Uph6fu1UdjI2r6gacgBurvJmrwZOT5yChp2i8+B99e+qFqWNUi9AI7Q==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-exec/-/cli-exec-1.29.4.tgz", + "integrity": "sha512-1UQVJ4j9LwY895GV6vQB9XHmNRscE0AgRKOkJZYsiQUSVQqfuA/gYXI1XFY6wuLYidmu02abviHmcoztTj5+0g==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.29.3", - "@percy/logger": "1.29.3", + "@percy/cli-command": "1.29.4", + "@percy/logger": "1.29.4", "cross-spawn": "^7.0.3", "which": "^2.0.2" }, @@ -6303,12 +6203,13 @@ } }, "node_modules/@percy/cli-snapshot": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.29.3.tgz", - "integrity": "sha512-5buoW+tSdfCu0Df7LYzOpjJlb9u+4aCTDrYlmwBcyEVUq01E39LN1kRWCYL6jU/APNB+ybUKtLr9w0RtcPDYTQ==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-snapshot/-/cli-snapshot-1.29.4.tgz", + "integrity": "sha512-/wgXOPHbHOjUuZklIl4HqeD1R5XM2PTUKmq5H9BMOZkbmLeUkl/0R2BBOmPqjVINfK6LaBS69PuVJjCtlwWCyA==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.29.3", + "@percy/cli-command": "1.29.4", "yaml": "^2.0.0" }, "engines": { @@ -6316,12 +6217,13 @@ } }, "node_modules/@percy/cli-upload": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.29.3.tgz", - "integrity": "sha512-KYAtcAzE50lbZ8NTqao/GSVurESUi2iQFCJ0zRwEccxViOJy/dfb5j1i10VPlqP5glCZS+eXXY2YYoxjxVCz3w==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/cli-upload/-/cli-upload-1.29.4.tgz", + "integrity": "sha512-Du5N/84n8ZnhGsM2ReLNB4E44DFn5F37XCRTAqweph+duP3PsodUmGq+/oFxX8RcdSizGJN1vGXFzxji+sAqiA==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/cli-command": "1.29.3", + "@percy/cli-command": "1.29.4", "fast-glob": "^3.2.11", "image-size": "^1.0.0" }, @@ -6330,13 +6232,14 @@ } }, "node_modules/@percy/client": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.29.3.tgz", - "integrity": "sha512-BTP/wfgs2/Hjj650tGmp+jkATEIOdNNZFZSRWPXGXF+PFG4zK5jTejBEZlBl3NUqhwMqtfLX/uyvsfKFaWfYDA==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/client/-/client-1.29.4.tgz", + "integrity": "sha512-PfWHOLdvOHuVO/rp7bC/XVMxl2be5kls/K9ZgDzliBZlGxblIn+hES2UfMm0MGIBQUVtfylVUkg6Xp7ppwlI7A==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/env": "1.29.3", - "@percy/logger": "1.29.3", + "@percy/env": "1.29.4", + "@percy/logger": "1.29.4", "pako": "^2.1.0" }, "engines": { @@ -6344,12 +6247,13 @@ } }, "node_modules/@percy/config": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.29.3.tgz", - "integrity": "sha512-Jk79XGpiCNI7gmdCoWkn5V7HVa6FFfcYvFg3H1OMd2BqZEDKkPq9bbk0e4AZ93xc2BOjmYWHHj69w7VCu1peug==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/config/-/config-1.29.4.tgz", + "integrity": "sha512-oU8yaoGUop9YV4rzVYa22hixARLS78XR+z3qXPPCFUMuWRAvQmjlJUkHfIj2RZizfHV9RmJW78B/bMQZbGHFfQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/logger": "1.29.3", + "@percy/logger": "1.29.4", "ajv": "^8.6.2", "cosmiconfig": "^8.0.0", "yaml": "^2.0.0" @@ -6362,13 +6266,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@percy/config/node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -6395,6 +6301,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -6403,25 +6310,26 @@ } }, "node_modules/@percy/core": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.29.3.tgz", - "integrity": "sha512-5RwQyezq/i4fqSMeoKJole/kZ7n7lABITS6py2e9een6svNFRKI8VqWBlMuFsL50Z5mUcbSJDhIl8O8NbIpdwQ==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/core/-/core-1.29.4.tgz", + "integrity": "sha512-wxhorVvdvz9H8saZEpcKImtZDx9aX6QT0GoJN3K3X/Lek9wvkihr43dEKjHK9cIfvnXE+TlEIYPJ/kHAVf3XRA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@percy/client": "1.29.3", - "@percy/config": "1.29.3", - "@percy/dom": "1.29.3", - "@percy/logger": "1.29.3", - "@percy/webdriver-utils": "1.29.3", + "@percy/client": "1.29.4", + "@percy/config": "1.29.4", + "@percy/dom": "1.29.4", + "@percy/logger": "1.29.4", + "@percy/webdriver-utils": "1.29.4", "content-disposition": "^0.5.4", "cross-spawn": "^7.0.3", "extract-zip": "^2.0.1", "fast-glob": "^3.2.11", - "micromatch": "^4.0.6", + "micromatch": "^4.0.8", "mime-types": "^2.1.34", "pako": "^2.1.0", - "path-to-regexp": "^6.2.0", + "path-to-regexp": "^6.3.0", "rimraf": "^3.0.2", "ws": "^8.17.1", "yaml": "^2.4.1" @@ -6431,37 +6339,41 @@ } }, "node_modules/@percy/dom": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.29.3.tgz", - "integrity": "sha512-wz5PV5IW/ooYTmeiq4qFDWyZrVoyp4x+cOQ4ndYStDMkiFMnN5zvvqJlSsUOJ9/YKh/BeMn+ed8hlfKOWW3zEQ==", - "dev": true + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/dom/-/dom-1.29.4.tgz", + "integrity": "sha512-PBHufYICgZSMOEdojiOwYWN9XbwVZvXWUS26cLD4LSDp5JkWlQ8rb7YkM9mowDUE/ROpz66Q4sCagV05sOWsbg==", + "dev": true, + "license": "MIT" }, "node_modules/@percy/env": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.29.3.tgz", - "integrity": "sha512-DwWsnrGWsBQkIuNvw//CNQpyd5LY2rzc6wqB/2GMpVf4iuzKvm5ND55GX8j0FYhf0kJan0aS/+mDKEgZfea1LA==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/env/-/env-1.29.4.tgz", + "integrity": "sha512-CZQphh+uuZb3OMD3FcyugzTtmHASy1xczoUIBqx753mR5bVGxmTAxtv/y65rkc2EngO3rjtFaaNbGn7Ze9asuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/logger": "1.29.3" + "@percy/logger": "1.29.4" }, "engines": { "node": ">=14" } }, "node_modules/@percy/logger": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.29.3.tgz", - "integrity": "sha512-nNslGmznG5ChKHFtPtRFcjAeuG/Zhr1OgRapLLeikyXyxy8bT929kUgBuGh4ADhp95iovcN7zlHQmpuwbOPQ2Q==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/logger/-/logger-1.29.4.tgz", + "integrity": "sha512-AKFqIl5hYKHJaG8hUP1hnzKCXgrluS/fb7h3pGkcWIVlWy3FMeishzwjoGSgmaz7xDtUlg1e0/I/u6oZjW+gag==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" } }, "node_modules/@percy/sdk-utils": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.29.3.tgz", - "integrity": "sha512-ITUZinf+50O/Izs/X3HaRxnZvLv4Fw8lV2mSqVD/500au6bApUNeMHnoaAHOC57FgyUOUaYldiAAXNtE/zANtw==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/sdk-utils/-/sdk-utils-1.29.4.tgz", + "integrity": "sha512-KKJ3oP0AeyJ0OIFl4HIv1qfmAKAaS1GwsrUUXFG/JDXI4KxIeVl0eKMnax8mITHa9Im1P8lMXSm8r8/hAPRKGg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" } @@ -6481,13 +6393,14 @@ } }, "node_modules/@percy/webdriver-utils": { - "version": "1.29.3", - "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.29.3.tgz", - "integrity": "sha512-+0qyRGKLfYdtzhc9U1m7RCBk6c3+aGy8DPLM6FdlvCrX5+Z93PLLMpoQcXid9cUFFTGAnxOKtYUWS2kc6Q32mg==", + "version": "1.29.4", + "resolved": "https://registry.npmjs.org/@percy/webdriver-utils/-/webdriver-utils-1.29.4.tgz", + "integrity": "sha512-Y4v83vbfPS8hb2+tXWba3lhdafNLMplbmmFEITYcaLaWLJlfe6CPzK1lSUWQGHg4swfDey4nD862Mc7e/VMykw==", "dev": true, + "license": "MIT", "dependencies": { - "@percy/config": "1.29.3", - "@percy/sdk-utils": "1.29.3" + "@percy/config": "1.29.4", + "@percy/sdk-utils": "1.29.4" }, "engines": { "node": ">=14" @@ -7726,9 +7639,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", - "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -7740,9 +7653,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", - "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -7754,9 +7667,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", - "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -7768,9 +7681,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", - "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -7782,9 +7695,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", - "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", "cpu": [ "arm" ], @@ -7796,9 +7709,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", - "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -7810,9 +7723,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", - "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -7824,9 +7737,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", - "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -7838,9 +7751,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", - "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", "cpu": [ "ppc64" ], @@ -7852,9 +7765,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", - "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -7866,9 +7779,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", - "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", "cpu": [ "s390x" ], @@ -7880,9 +7793,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", - "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -7894,9 +7807,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", - "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -7908,9 +7821,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", - "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -7922,9 +7835,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", - "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -7936,9 +7849,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", - "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -7950,14 +7863,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.1.tgz", - "integrity": "sha512-bBV7I+MCbdQmBPUFF4ECg37VReM0+AdQsxgwkjBBSYExmkErkDoDgKquwL/tH7stDCc5IfTd0g9BMeosRgDMug==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.7.tgz", + "integrity": "sha512-WOBzO11qstznHbC9tZXQf6/8+PqmaRI6QYcdTspqXNh9q9nNglvi43Xn4tSIpEhW8aSHea9hgWZV8sG+i/4W9Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.1", - "@angular-devkit/schematics": "18.2.1", + "@angular-devkit/core": "18.2.7", + "@angular-devkit/schematics": "18.2.7", "jsonc-parser": "3.3.1" }, "engines": { @@ -7966,53 +7879,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.1.tgz", - "integrity": "sha512-fSuGj6CxiTFR+yjuVcaWqaVb5Wts39CSBYRO1BlsOlbuWFZ2NKC/BAb5bdxpB31heCBJi7e3XbPvcMMJIcnKlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.1.tgz", - "integrity": "sha512-2t/q0Jcv7yqhAzEdNgsxoGSCmPgD4qfnVOJ7EJw3LNIA+kX1CmtN4FESUS0i49kN4AyNJFAI5O2pV8iJiliKaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "18.2.1", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.11", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@schematics/angular/node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -8021,73 +7887,73 @@ "license": "MIT" }, "node_modules/@sentry-internal/browser-utils": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.26.0.tgz", - "integrity": "sha512-O2Tj+WK33/ZVp5STnz6ZL0OO+/Idk2KqsH0ITQkQmyZ2z0kdzWOeqK7s7q3/My6rB1GfPcyqPcBBv4dVv92FYQ==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.32.0.tgz", + "integrity": "sha512-DpUGhk5O1OVjT0fo9wsbEdO1R/S9gGBRDtn9+FFVeRtieJHwXpeZiLK+tZhTOvaILmtSoTPUEY3L5sK4j5Xq9g==", "license": "MIT", "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.32.0", + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/feedback": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.26.0.tgz", - "integrity": "sha512-hQtw1gg8n6ERK1UH47F7ZI1zOsbhu0J2VX+TrnkpaQR2FgxDW1oe9Ja6oCV4CQKuR4w+1ZI/Kj4imSt0K33kEw==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.32.0.tgz", + "integrity": "sha512-XB7hiVJQW1tNzpoXIHbvm3rjipIt7PZiJJtFg2vxaqu/FzdgOcYqQiwIKivJVAKuRZ9rIeJtK1jdXQFOc/TRJA==", "license": "MIT", "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.32.0", + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.26.0.tgz", - "integrity": "sha512-JDY7W2bswlp5c3483lKP4kcb75fHNwGNfwD8x8FsY9xMjv7nxeXjLpR5cCEk1XqPq2+n6w4j7mJOXhEXGiUIKg==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.32.0.tgz", + "integrity": "sha512-yiEUnn2yyo1AIQIFNeRX3tdK8fmyKIkxdFS1WiVQmeYI/hFwYBTZPly0FcO/g3xnRMSA2tvrS+hZEaaXfK4WhA==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.32.0", + "@sentry/core": "8.32.0", + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.26.0.tgz", - "integrity": "sha512-2CFQW6f9aJHIo/DqmqYa9PaYoLn1o36ywc0h8oyGrD4oPCbrnE5F++PmTdc71GBODu41HBn/yoCTLmxOD+UjpA==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.32.0.tgz", + "integrity": "sha512-oBbhtDBkD+5z/T0NVJ5VenBWAid/S9QdVrod/UqxVqU7F8N+E9/INFQI48zCWr4iVlUMcszJPDElvJEsMDvvBQ==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/replay": "8.32.0", + "@sentry/core": "8.32.0", + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/angular": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.26.0.tgz", - "integrity": "sha512-9YolcJMdEzS6hbImal3jrAbzGZGM7DpmfSOfzt1Cs4bYTD9gCxKRkLyUgiNRIlrIBO7CkdpMrCSY+nEohvCw7A==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.32.0.tgz", + "integrity": "sha512-HgdpLFTdAMgTG4yz6mb9umg+yGlCkuRDqC4Wv1zNW7ARoSioavyz4kMRkKqJR6hxgGh2vPoXCz6E+w8L4k9oPg==", "license": "MIT", "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.32.0", + "@sentry/core": "8.32.0", + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0", "tslib": "^2.4.1" }, "engines": { @@ -8101,52 +7967,52 @@ } }, "node_modules/@sentry/browser": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.26.0.tgz", - "integrity": "sha512-e5s6eKlwLZWzTwQcBwqyAGZMMuQROW9Z677VzwkSyREWAIkKjfH2VBxHATnNGc0IVkNHjD7iH3ixo3C0rLKM3w==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.32.0.tgz", + "integrity": "sha512-AEKFj64g4iYwEMRvVcxiY0FswmClRXCP1IEvCqujn8OBS8AjMOr1z/RwYieEs0D90yNNB3YEqF8adrKENblJmw==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry-internal/feedback": "8.26.0", - "@sentry-internal/replay": "8.26.0", - "@sentry-internal/replay-canvas": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.32.0", + "@sentry-internal/feedback": "8.32.0", + "@sentry-internal/replay": "8.32.0", + "@sentry-internal/replay-canvas": "8.32.0", + "@sentry/core": "8.32.0", + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/core": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.26.0.tgz", - "integrity": "sha512-g/tVmTZD4GNbLFf++hKJfBpcCAtduFEMLnbfa9iT/QEZjlmP+EzY+GsH9bafM5VsNe8DiOUp+kJKWtShzlVdBA==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.32.0.tgz", + "integrity": "sha512-+xidTr0lZ0c755tq4k75dXPEb8PA+qvIefW3U9+dQMORLokBrYoKYMf5zZTG2k/OfSJS6OSxatUj36NFuCs3aA==", "license": "MIT", "dependencies": { - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/types": "8.32.0", + "@sentry/utils": "8.32.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/types": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.26.0.tgz", - "integrity": "sha512-zKmh6SWsJh630rpt7a9vP4Cm4m1C2gDTUqUiH565CajCL/4cePpNWYrNwalSqsOSL7B9OrczA1+n6a6XvND+ng==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.32.0.tgz", + "integrity": "sha512-hxckvN2MzS5SgGDgVQ0/QpZXk13Vrq4BtZLwXhPhyeTmZtUiUfWvcL5TFQqLinfKdTKPe9q2MxeAJ0D4LalhMg==", "license": "MIT", "engines": { "node": ">=14.18" } }, "node_modules/@sentry/utils": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.26.0.tgz", - "integrity": "sha512-xvlPU9Hd2BlyT+FhWHGNwnxWqdVRk2AHnDtVcW4Ma0Ri5EwS+uy4Jeik5UkSv8C5RVb9VlxFmS8LN3I1MPJsLw==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.32.0.tgz", + "integrity": "sha512-t1WVERhgmYURxbBj9J4/H2P2X+VKqm7B3ce9iQyrZbdf5NekhcU4jHIecPUWCPHjQkFIqkVTorqeBmDTlg/UmQ==", "license": "MIT", "dependencies": { - "@sentry/types": "8.26.0" + "@sentry/types": "8.32.0" }, "engines": { "node": ">=14.18" @@ -10857,21 +10723,13 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -11717,6 +11575,7 @@ "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.6.0" }, @@ -12098,9 +11957,10 @@ } }, "node_modules/angulartics2": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/angulartics2/-/angulartics2-14.0.0.tgz", - "integrity": "sha512-9w3t0T4KlvW395QBVdUtuXrli7/HaZNwy/KnaLmsFqlVF6f/bu//dzQuZ63VgO2lgwnCOvVquieq5UaIa12cIA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/angulartics2/-/angulartics2-14.1.0.tgz", + "integrity": "sha512-7DzKIUX6a3HzkEJjx4euDZiRbuZpU7HRF38slz5iTMthGsCdUXNNetHSRybFx4soi2uybd5lx7tE0bFAjuh48A==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -16122,17 +15982,17 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -16741,10 +16601,11 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", - "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -16758,7 +16619,7 @@ "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", @@ -16767,11 +16628,11 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", - "serve-static": "1.16.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -16787,6 +16648,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -16801,13 +16663,14 @@ } }, "node_modules/express/node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -16818,20 +16681,12 @@ "node": ">= 0.8" } }, - "node_modules/express/node_modules/finalhandler/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/express/node_modules/path-to-regexp": { "version": "0.1.10", @@ -16839,21 +16694,6 @@ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "dev": true }, - "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -18257,21 +18097,6 @@ "wbuf": "^1.1.0" } }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/html-entities": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", @@ -18631,6 +18456,7 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", "dev": true, + "license": "MIT", "dependencies": { "queue": "6.0.2" }, @@ -19639,10 +19465,11 @@ } }, "node_modules/jasmine-core": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz", - "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==", - "dev": true + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.3.0.tgz", + "integrity": "sha512-zsOmeBKESky4toybvWEikRiZ0jHoBEu79wNArLfMdSnlLMZx3Xcp6CSm2sUcYyoJC+Uyj8LBJap/MUbVSfJ27g==", + "dev": true, + "license": "MIT" }, "node_modules/jasmine-spec-reporter": { "version": "7.0.0", @@ -20071,10 +19898,11 @@ } }, "node_modules/karma": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", - "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, + "license": "MIT", "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -20437,9 +20265,9 @@ } }, "node_modules/launch-editor": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.1.tgz", - "integrity": "sha512-elBx2l/tp9z99X5H/qev8uyDywVh0VXAwEbjk8kJhnc5grOFkGh7aW6q55me9xnYbss261XtnUrysZ+XvGbhQA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", "dev": true, "license": "MIT", "dependencies": { @@ -22148,6 +21976,7 @@ "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "license": "MIT", "engines": { "node": "*" } @@ -22209,6 +22038,7 @@ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -22402,18 +22232,18 @@ "dev": true }, "node_modules/ngx-markdown": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-18.0.0.tgz", - "integrity": "sha512-sFR9dIOKobdhNKZTlCrX3RmpoAhZ7k3T9h7oWJP676Oe9BsoxuAYZKJmFDT20vrY6xmFD3WtLJDZR7rNRLf6Uw==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-18.1.0.tgz", + "integrity": "sha512-n4HFSm5oqVMXFuD+WXIVkI6NyxD8Oubr4B3c9U1J7Ptr6t9DVnkNBax3yxWc+8Wli+FXTuGEnDXzB3sp7E9paA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "optionalDependencies": { "clipboard": "^2.0.11", - "emoji-toolkit": "^8.0.0", + "emoji-toolkit": ">= 8.0.0 < 10.0.0", "katex": "^0.16.0", - "mermaid": "^10.6.0", + "mermaid": ">= 10.6.0 < 12.0.0", "prismjs": "^1.28.0" }, "peerDependencies": { @@ -22577,9 +22407,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "dev": true, "license": "MIT", "optional": true, @@ -23222,9 +23052,9 @@ } }, "node_modules/ordered-binary": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", - "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.2.tgz", + "integrity": "sha512-JTo+4+4Fw7FreyAvlSLjb1BBVaxEQAacmjD3jjuyPZclpbEghTvQZbXBb2qPd2LeIMxiHwXBZUcpmG2Gl/mDEA==", "dev": true, "license": "MIT" }, @@ -23384,7 +23214,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "dev": true + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/papaparse": { "version": "5.4.1", @@ -23469,6 +23300,7 @@ "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", "dev": true, + "license": "MIT", "dependencies": { "entities": "^4.3.0", "parse5": "^7.0.0", @@ -23496,6 +23328,7 @@ "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", "dev": true, + "license": "MIT", "dependencies": { "parse5": "^7.0.0" }, @@ -23584,10 +23417,11 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", - "dev": true + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -23664,9 +23498,10 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.2", @@ -24544,6 +24379,7 @@ "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "~2.0.3" } @@ -25512,9 +25348,9 @@ "optional": true }, "node_modules/rollup": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", - "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dev": true, "license": "MIT", "dependencies": { @@ -25528,22 +25364,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.20.0", - "@rollup/rollup-android-arm64": "4.20.0", - "@rollup/rollup-darwin-arm64": "4.20.0", - "@rollup/rollup-darwin-x64": "4.20.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", - "@rollup/rollup-linux-arm-musleabihf": "4.20.0", - "@rollup/rollup-linux-arm64-gnu": "4.20.0", - "@rollup/rollup-linux-arm64-musl": "4.20.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", - "@rollup/rollup-linux-riscv64-gnu": "4.20.0", - "@rollup/rollup-linux-s390x-gnu": "4.20.0", - "@rollup/rollup-linux-x64-gnu": "4.20.0", - "@rollup/rollup-linux-x64-musl": "4.20.0", - "@rollup/rollup-win32-arm64-msvc": "4.20.0", - "@rollup/rollup-win32-ia32-msvc": "4.20.0", - "@rollup/rollup-win32-x64-msvc": "4.20.0", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, @@ -25660,6 +25496,8 @@ "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -25947,75 +25785,29 @@ } }, "node_modules/serve-static": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", - "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dev": true, + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/serve-static/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-static/node_modules/debug/node_modules/ms": { + "node_modules/serve-static/node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/serve-static/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/serve-static/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/serve-static/node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8" } }, "node_modules/set-cookie-parser": { @@ -26324,10 +26116,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -26846,92 +26639,6 @@ "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", "optional": true }, - "node_modules/stylus": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", - "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@adobe/css-tools": "^4.0.1", - "debug": "^4.3.2", - "glob": "^7.1.6", - "sax": "~1.2.4", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://opencollective.com/stylus" - } - }, - "node_modules/stylus/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/stylus/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/stylus/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, "node_modules/sublevel-pouchdb": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/sublevel-pouchdb/-/sublevel-pouchdb-9.0.0.tgz", @@ -27852,9 +27559,10 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" }, "node_modules/tsutils": { "version": "3.21.0", @@ -28618,15 +28326,15 @@ } }, "node_modules/vite": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", - "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.40", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -29107,6 +28815,35 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/vite/node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -29378,9 +29115,9 @@ } }, "node_modules/webpack-dev-server/node_modules/memfs": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", - "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.12.0.tgz", + "integrity": "sha512-74wDsex5tQDSClVkeK1vtxqYCAgCoXxx+K4NSHzgU/muYVYByFqa+0RnrPO9NM6naWm1+G9JmZ0p6QHhXmeYfA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -29433,9 +29170,9 @@ } }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.1.tgz", - "integrity": "sha512-/t6KpZw/bnmCR0VKILjJT05mWecbf1aIM2VxCJUvBbg0iXqaQJFxbJ4PCrsY4iBH7PGwnccm4BYyoP1G+lGfAA==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", "dev": true, "license": "MIT", "dependencies": { @@ -29944,6 +29681,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "dev": true, + "license": "ISC", "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index 0dff6f5f3a..3d0df9d7b7 100644 --- a/package.json +++ b/package.json @@ -23,30 +23,30 @@ "stream": "./node_modules/stream-browserify" }, "dependencies": { - "@angular/animations": "^18.2.0", - "@angular/cdk": "^18.2.0", - "@angular/common": "^18.2.0", - "@angular/compiler": "^18.2.0", - "@angular/core": "^18.2.0", - "@angular/forms": "^18.2.0", - "@angular/localize": "^18.2.0", - "@angular/material": "^18.2.0", - "@angular/material-moment-adapter": "^18.2.0", - "@angular/platform-browser": "^18.2.0", - "@angular/platform-browser-dynamic": "^18.2.0", - "@angular/router": "^18.2.0", - "@angular/service-worker": "^18.2.0", + "@angular/animations": "^18.2.7", + "@angular/cdk": "^18.2.6", + "@angular/common": "^18.2.7", + "@angular/compiler": "^18.2.7", + "@angular/core": "^18.2.7", + "@angular/forms": "^18.2.7", + "@angular/localize": "^18.2.7", + "@angular/material": "^18.2.6", + "@angular/material-moment-adapter": "^18.2.6", + "@angular/platform-browser": "^18.2.7", + "@angular/platform-browser-dynamic": "^18.2.7", + "@angular/router": "^18.2.7", + "@angular/service-worker": "^18.2.7", "@aytek/material-color-picker": "^1.0.4", "@casl/ability": "^6.7.1", - "@casl/angular": "^8.2.7", + "@casl/angular": "^8.2.8", "@faker-js/faker": "^8.4.1", "@fortawesome/angular-fontawesome": "^0.15.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@ngneat/until-destroy": "^10.0.0", - "@sentry/angular": "^8.26.0", - "angulartics2": "^14.0.0", + "@sentry/angular": "^8.32.0", + "angulartics2": "^14.1.0", "assert": "^2.1.0", "crypto-es": "^2.1.0", "deep-object-diff": "^1.1.9", @@ -66,27 +66,27 @@ "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "stream-browserify": "^3.0.0", - "tslib": "^2.6.3", + "tslib": "^2.7.0", "util": "^0.12.5", "uuid": "^10.0.0", "webpack": "^5.94.0", "zone.js": "^0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.0", - "@angular-eslint/builder": "^18.3.0", - "@angular-eslint/eslint-plugin": "^18.3.0", - "@angular-eslint/eslint-plugin-template": "^18.3.0", - "@angular-eslint/schematics": "^18.3.0", - "@angular-eslint/template-parser": "^18.3.0", - "@angular/cli": "^18.2.0", - "@angular/compiler-cli": "^18.2.0", - "@babel/core": "^7.24.9", + "@angular-devkit/build-angular": "^18.2.7", + "@angular-eslint/builder": "^18.3.1", + "@angular-eslint/eslint-plugin": "^18.3.1", + "@angular-eslint/eslint-plugin-template": "^18.3.1", + "@angular-eslint/schematics": "^18.3.1", + "@angular-eslint/template-parser": "^18.3.1", + "@angular/cli": "^18.2.7", + "@angular/compiler-cli": "^18.2.7", + "@babel/core": "^7.25.2", "@compodoc/compodoc": "^1.1.25", "@cypress/schematic": "~2.5.2", - "@percy/cli": "^1.29.0", + "@percy/cli": "^1.29.4", "@percy/storybook": "^5.0.3", - "@schematics/angular": "^18.2.1", + "@schematics/angular": "^18.2.7", "@storybook/addon-actions": "^7.6.20", "@storybook/addon-essentials": "^7.6.20", "@storybook/angular": "^7.6.20", @@ -103,13 +103,13 @@ "@typescript-eslint/parser": "^8.2.0", "babel-loader": "^9.1.3", "cypress": "~13.14.0", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-storybook": "^0.8.0", - "jasmine-core": "^5.2.0", + "jasmine-core": "^5.3.0", "jasmine-spec-reporter": "^7.0.0", - "karma": "^6.4.3", + "karma": "^6.4.4", "karma-chrome-launcher": "^3.2.0", "karma-cli": "^2.0.0", "karma-coverage": "^2.2.1", From 42a78b34494e985e10f5898797cd5175c9e682b1 Mon Sep 17 00:00:00 2001 From: Sebastian Leidig Date: Wed, 2 Oct 2024 19:22:12 +0530 Subject: [PATCH 11/11] fix(i18n): update missing translations --- src/assets/locale/messages.de.xlf | 42 ++++++++++++++++++--------- src/assets/locale/messages.fr.xlf | 42 ++++++++++++++++++--------- src/assets/locale/messages.it.xlf | 42 ++++++++++++++++++--------- src/assets/locale/messages.xlf | 48 ++++++++++++++++++++----------- 4 files changed, 118 insertions(+), 56 deletions(-) diff --git a/src/assets/locale/messages.de.xlf b/src/assets/locale/messages.de.xlf index ad0b213e9e..795f940acf 100644 --- a/src/assets/locale/messages.de.xlf +++ b/src/assets/locale/messages.de.xlf @@ -1671,15 +1671,15 @@ "" wird hochgeladen src/app/features/file/couchdb-file.service.ts - 72 + 57 Loading "" "" wird heruntergeladen - src/app/features/file/couchdb-file.service.ts - 172 + src/app/features/file/file.service.ts + 113 @@ -3552,12 +3552,28 @@ 47,53 + + Color + Farbe + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 59 + + + + The color to represent this entity type, e.g. when displaying records as pins on the map. + Die Farbe für diesen Typ, z.B. für die Anzeige von Markierungen auf der Karte. + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 63 + + Generated Title of Record Generierter Titel eines Datensatzes src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 61 + 74 @@ -3565,7 +3581,7 @@ Die Werte der ausgewählten Felder werden (in dieser Reihenfolge) genutzt um eine Bezeichnung für einen konkreten Datensatz zu erzeugen. (Nur Felder vom Typ "Text" sind hierfür verfügbar) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 65 + 78 @@ -3573,7 +3589,7 @@ Anonymisierung konfigurieren src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 87 + 100 @@ -3581,7 +3597,7 @@ Enthält persönliche Daten (DSGVO-relevant) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 97 + 110 @@ -3589,7 +3605,7 @@ Auswählen, wenn Angaben dieses Datentyps persönliche Daten enthalten können. Diese Option aktiviert die "Anonymisierungs-Funktion". Damit können Nutzer:innen einen Datensatz dieses Typs anonymisieren, anstatt diesen nur zu archivieren oder komplett zu löschen. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 100 + 113 @@ -3597,7 +3613,7 @@ Einstellungen wie Datensätze dieses Typs anonymisiert werden. Nutzer:innen können einen Eintrag "anonymisieren" als Alternative dazu ihn zu archivieren (wobei alle persönlichen Daten gespeichert bleiben) oder zu löschen (wobei jegliche Spur des Datensatzes entfernt wird, selbst ihn Berichten und sonstigen Auswertungen). Die Liste aller Profilfelder hier definiert, welche Felder "erhalten" bleiben können, um weiterhin statistische Auswertungen zu ermöglichen, und welche Felder entfernt werden müssen, da sie persönliche Daten enthalten. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 106,114 + 119 @@ -6120,7 +6136,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi - Bulk edit + Bulk edit Bearbeiten von @@ -6166,11 +6182,11 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi edited bearbeitet + Entity action confirmation message verb src/app/core/entity/entity-actions/entity-edit.service.ts - 53 + 58 - Entity action confirmation message verb Undo @@ -6226,7 +6242,7 @@ Zögern Sie nicht uns mit Fragen und Feedback zu kontaktieren: [support@aam-digi Entity.toString fallback for anonymized record src/app/core/entity/model/entity.ts - 313 + 320 diff --git a/src/assets/locale/messages.fr.xlf b/src/assets/locale/messages.fr.xlf index a6cc6a1483..86894e24b9 100644 --- a/src/assets/locale/messages.fr.xlf +++ b/src/assets/locale/messages.fr.xlf @@ -3184,12 +3184,28 @@ 47,53 + + Color + Color + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 59 + + + + The color to represent this entity type, e.g. when displaying records as pins on the map. + The color to represent this entity type, e.g. when displaying records as pins on the map. + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 63 + + Generated Title of Record Generated Title of Record src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 61 + 74 @@ -3197,7 +3213,7 @@ Select the fields that should be used (in that order) to generate a simple name/title for a record. This generated title is used in previews, search and for form fields that allow to select a record of this type. (Only text fields can be used here) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 65 + 78 @@ -3205,7 +3221,7 @@ Configure PII / Anonymization src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 87 + 100 @@ -3213,7 +3229,7 @@ Has personal information (PII) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 97 + 110 @@ -3221,7 +3237,7 @@ If the fields of this record type contain personal, sensitive information you can mark this here. Checking this box enables the 'anonymization' feature. This allows users to anonymize records of this type instead of just archiving or deleting them. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 100 + 113 @@ -3229,7 +3245,7 @@ Configure how records of this type can be anonymized. Users can "anonymize" a record as an alternative to just archiving it (keeping all personal details) or deleting it (losing any trace of the record, even in reports). Select below which fields can be "retained" to keep some limited data for statistical reporting and which fields have to be removed because they contain personal details. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 106,114 + 119 @@ -6112,7 +6128,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit - Bulk edit + Bulk edit Bulk edit @@ -6158,11 +6174,11 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit edited edited + Entity action confirmation message verb src/app/core/entity/entity-actions/entity-edit.service.ts - 53 + 58 - Entity action confirmation message verb Undo @@ -6218,7 +6234,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Entity.toString fallback for anonymized record src/app/core/entity/model/entity.ts - 313 + 320 @@ -7711,15 +7727,15 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Uploading "" src/app/features/file/couchdb-file.service.ts - 72 + 57 Loading "" Loading "" - src/app/features/file/couchdb-file.service.ts - 172 + src/app/features/file/file.service.ts + 113 diff --git a/src/assets/locale/messages.it.xlf b/src/assets/locale/messages.it.xlf index 37e059d406..ca398ab1f4 100644 --- a/src/assets/locale/messages.it.xlf +++ b/src/assets/locale/messages.it.xlf @@ -2815,12 +2815,28 @@ 47,53 + + Color + Color + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 59 + + + + The color to represent this entity type, e.g. when displaying records as pins on the map. + The color to represent this entity type, e.g. when displaying records as pins on the map. + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 63 + + Generated Title of Record Generated Title of Record src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 61 + 74 @@ -2828,7 +2844,7 @@ Select the fields that should be used (in that order) to generate a simple name/title for a record. This generated title is used in previews, search and for form fields that allow to select a record of this type. (Only text fields can be used here) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 65 + 78 @@ -2836,7 +2852,7 @@ Configure PII / Anonymization src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 87 + 100 @@ -2844,7 +2860,7 @@ Has personal information (PII) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 97 + 110 @@ -2852,7 +2868,7 @@ If the fields of this record type contain personal, sensitive information you can mark this here. Checking this box enables the 'anonymization' feature. This allows users to anonymize records of this type instead of just archiving or deleting them. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 100 + 113 @@ -2860,7 +2876,7 @@ Configure how records of this type can be anonymized. Users can "anonymize" a record as an alternative to just archiving it (keeping all personal details) or deleting it (losing any trace of the record, even in reports). Select below which fields can be "retained" to keep some limited data for statistical reporting and which fields have to be removed because they contain personal details. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 106,114 + 119 @@ -5716,7 +5732,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit - Bulk edit + Bulk edit Bulk edit @@ -5762,11 +5778,11 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit edited edited + Entity action confirmation message verb src/app/core/entity/entity-actions/entity-edit.service.ts - 53 + 58 - Entity action confirmation message verb Undo @@ -5822,7 +5838,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Entity.toString fallback for anonymized record src/app/core/entity/model/entity.ts - 313 + 320 @@ -7404,15 +7420,15 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Uploading "" src/app/features/file/couchdb-file.service.ts - 72 + 57 Loading "" Loading "" - src/app/features/file/couchdb-file.service.ts - 172 + src/app/features/file/file.service.ts + 113 diff --git a/src/assets/locale/messages.xlf b/src/assets/locale/messages.xlf index cd48135c3b..4c6756c14f 100644 --- a/src/assets/locale/messages.xlf +++ b/src/assets/locale/messages.xlf @@ -2273,46 +2273,60 @@ 47,53 + + Color + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 59 + + + + The color to represent this entity type, e.g. when displaying records as pins on the map. + + src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html + 63 + + Generated Title of Record src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 61 + 74 Select the fields that should be used (in that order) to generate a simple name/title for a record. This generated title is used in previews, search and for form fields that allow to select a record of this type. (Only text fields can be used here) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 65 + 78 Configure PII / Anonymization src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 87 + 100 Has personal information (PII) src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 97 + 110 If the fields of this record type contain personal, sensitive information you can mark this here. Checking this box enables the 'anonymization' feature. This allows users to anonymize records of this type instead of just archiving or deleting them. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 100 + 113 Configure how records of this type can be anonymized. Users can "anonymize" a record as an alternative to just archiving it (keeping all personal details) or deleting it (losing any trace of the record, even in reports). Select below which fields can be "retained" to keep some limited data for statistical reporting and which fields have to be removed because they contain personal details. src/app/core/admin/admin-entity/admin-entity-general-settings/admin-entity-general-settings.component.html - 106,114 + 119,127 @@ -5112,7 +5126,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Entity action confirmation message - Bulk edit + Bulk edit src/app/core/entity/entity-actions/entity-bulk-edit/entity-bulk-edit.component.html @@ -5153,7 +5167,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit edited src/app/core/entity/entity-actions/entity-edit.service.ts - 53 + 58 Entity action confirmation message verb @@ -5161,7 +5175,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit [anonymized ] src/app/core/entity/model/entity.ts - 313,315 + 320,322 Entity.toString fallback for anonymized record @@ -6715,14 +6729,7 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit Uploading "" src/app/features/file/couchdb-file.service.ts - 72 - - - - Loading "" - - src/app/features/file/couchdb-file.service.ts - 172 + 57 @@ -6808,6 +6815,13 @@ Feel free to reach out to us with your questions or feedback: [support@aam-digit datatype-label + + Loading "" + + src/app/features/file/file.service.ts + 113 + + profile photo